Java?GenericObjectPool?對象池化技術(shù)之SpringBoot?sftp?連接池工具類詳解
Java BasePooledObjectFactory 對象池化技術(shù)
通常一個對象創(chuàng)建、銷毀非常耗時的時候,我們不會頻繁的創(chuàng)建和銷毀它,而是考慮復(fù)用。復(fù)用對象的一種做法就是對象池,將創(chuàng)建好的對象放入池中維護(hù)起來,下次再用的時候直接拿池中已經(jīng)創(chuàng)建好的對象繼續(xù)用,這就是池化的思想。
Apache Commons Pool是一個對象池的框架,他提供了一整套用于實現(xiàn)對象池化的API。它提供了三種對象池:GenericKeyedObjectPool,SoftReferenceObjectPool和GenericObjectPool,其中GenericObjectPool是我們最常用的對象池,內(nèi)部實現(xiàn)也最復(fù)雜。
GenericObjectPool
GenericObjectPool 是一個通用對象池框架,我們可以借助它實現(xiàn)一個健壯的對象池,UML圖如下所示:

GenericObjectPool 實現(xiàn)了ObjectPool接口,而ObjectPool就是對象池的核心接口,它定義了一個對象池應(yīng)該實現(xiàn)的行為。
public interface ObjectPool<T> extends Closeable {
/**
* 從池中借走到一個對象
*/
T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
/**
* 把對象歸還給對象池
*/
void returnObject(T var1) throws Exception;
/**
* 驗證對象的有效性
*/
void invalidateObject(T var1) throws Exception;
/**
* 往池中添加一個對象
*/
void addObject() throws Exception, IllegalStateException, UnsupportedOperationException;
/**
* 返回對象池中有多少對象是空閑的,也就是能夠被借走的對象的數(shù)量。
*/
int getNumIdle();
/**
* 返回對象池中有對象對象是活躍的,也就是已經(jīng)被借走的,在使用中的對象的數(shù)量。
*/
int getNumActive();
/**
* 清理對象池。注意是清理不是清空,該方法要求的是,清理所有空閑對象,釋放相關(guān)資源。
*/
void clear() throws Exception, UnsupportedOperationException;
/**
* 關(guān)閉對象池。這個方法可以達(dá)到清空的效果,清理所有對象以及相關(guān)資源。
*/
void close();
}
BasePooledObjectFactory
Java BasePooledObjectFactory 對象池化技術(shù)
使用GenericObjectPool只需要創(chuàng)建一個對象工廠類,繼承BasePooledObjectFactory并重寫它的create()和destroyObject()。
如下文中的:SftpPool.java
public interface PooledObjectFactory<T> {
/**
* 創(chuàng)建一個可由池提供服務(wù)的實例,并將其封裝在由池管理的PooledObject中。
*/
PooledObject<T> makeObject() throws Exception;
/**
* 銷毀池不再需要的實例
*/
void destroyObject(PooledObject<T> var1) throws Exception;
/**
* 確保實例可以安全地由池返回
*/
boolean validateObject(PooledObject<T> var1);
/**
* 重新初始化池返回的實例
*/
void activateObject(PooledObject<T> var1) throws Exception;
/**
* 取消初始化要返回到空閑對象池的實例
*/
void passivateObject(PooledObject<T> var1) throws Exception;
}配置類GenericObjectPoolConfig
GenericObjectPoolConfig是封裝GenericObject池配置的簡單“結(jié)構(gòu)”,此類不是線程安全的;它僅用于提供創(chuàng)建池時使用的屬性。大多數(shù)情況,可以使用GenericObjectPoolConfig提供的默認(rèn)參數(shù)就可以滿足日常的需求。
工作原理流程
- 構(gòu)造方法
當(dāng)我們執(zhí)行構(gòu)造方法時,主要工作就是創(chuàng)建了一個存儲對象的LinkedList類型容器,也就是概念意義上的“池” - 從對象池中獲取對象
獲取池中的對象是通過borrowObject()命令,源碼比較復(fù)雜,簡單而言就是去LinkedList中獲取一個對象,如果不存在的話,要調(diào)用構(gòu)造方法中第一個參數(shù)Factory工廠類的makeObject()方法去創(chuàng)建一個對象再獲取,獲取到對象后要調(diào)用validateObject方法判斷該對象是否是可用的,如果是可用的才拿去使用。LinkedList容器減一 - 歸還對象到線程池
簡單而言就是先調(diào)用validateObject方法判斷該對象是否是可用的,如果可用則歸還到池中,LinkedList容器加一,如果是不可以的則調(diào)用destroyObject方法進(jìn)行銷毀
上面三步就是最簡單的流程,由于取和還的流程步驟都在borrowObject和returnObject方法中固定的,所以我們只要重寫Factory工廠類的makeObject()和validateObject以及destroyObject方法即可實現(xiàn)最簡單的池的管理控制,通過構(gòu)造方法傳入該Factory工廠類對象則可以創(chuàng)建最簡單的對象池管理類。這算是比較好的解耦設(shè)計模式,借和還的流程如下圖所示:

使用Demo
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>vipsoft-parent</artifactId>
<groupId>com.vipsoft.boot</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>vipsoft-sftp</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yaml
server:
port: 8088
application:
name: sftp Demo
sftp:
host: 172.16.3.88 # 服務(wù)器ip
port: 22 # ssh端口
username: root # 用戶名
password: root # 密碼
# 連接池參數(shù)
pool:
max-total: 10
max-idle: 10
min-idle: 5SftpPoolException.java
package com.vipsoft.sftp.exception;
/**
* sftp連接池異常
*/
public class SftpPoolException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Constructs a new runtime exception with {@code null} as its
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*/
public SftpPoolException() {
}
/**
* Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public SftpPoolException(String message) {
super(message);
}
/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public SftpPoolException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new runtime exception with the specified cause and a
* detail message of <tt>(cause==null ? null : cause.toString())</tt>
* (which typically contains the class and detail message of
* <tt>cause</tt>). This constructor is useful for runtime exceptions
* that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public SftpPoolException(Throwable cause) {
super(cause);
}
/**
* Constructs a new runtime exception with the specified detail
* message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
* @since 1.7
*/
public SftpPoolException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
config
SftpConfig.java
package com.vipsoft.sftp.config;
import com.vipsoft.sftp.pool.SftpFactory;
import com.vipsoft.sftp.pool.SftpPool;
import com.vipsoft.sftp.utils.SftpUtil;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(SftpProperties.class)
public class SftpConfig {
// 工廠
@Bean
public SftpFactory sftpFactory(SftpProperties properties) {
return new SftpFactory(properties);
}
// 連接池
@Bean
public SftpPool sftpPool(SftpFactory sftpFactory) {
return new SftpPool(sftpFactory);
}
// 輔助類
@Bean
public SftpUtil sftpUtil(SftpPool sftpPool) {
return new SftpUtil(sftpPool);
}
}
SftpProperties.java
package com.vipsoft.sftp.config;
import com.jcraft.jsch.ChannelSftp;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "sftp")
public class SftpProperties {
private String host;
private int port = 22;
private String username = "root";
private String password = "root";
private Pool pool = new Pool();
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Pool getPool() {
return pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
public static class Pool extends GenericObjectPoolConfig<ChannelSftp> {
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;
public Pool() {
super();
}
@Override
public int getMaxTotal() {
return maxTotal;
}
@Override
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
@Override
public int getMaxIdle() {
return maxIdle;
}
@Override
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
@Override
public int getMinIdle() {
return minIdle;
}
@Override
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
}
}Pool
SftpFactory.java
package com.vipsoft.sftp.pool;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.vipsoft.sftp.config.SftpProperties;
import com.vipsoft.sftp.exception.SftpPoolException;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
public class SftpFactory extends BasePooledObjectFactory<ChannelSftp> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private SftpProperties properties;
public SftpProperties getProperties() {
return properties;
}
public void setProperties(SftpProperties properties) {
this.properties = properties;
}
public SftpFactory(SftpProperties properties) {
this.properties = properties;
}
@Override
public ChannelSftp create() {
try {
JSch jsch = new JSch();
Session sshSession = jsch.getSession(properties.getUsername(), properties.getHost(), properties.getPort());
sshSession.setPassword(properties.getPassword());
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
sshSession.connect();
ChannelSftp channel = (ChannelSftp) sshSession.openChannel("sftp");
channel.connect();
return channel;
} catch (JSchException e) {
throw new SftpPoolException("連接sfpt失敗", e);
}
}
@Override
public PooledObject<ChannelSftp> wrap(ChannelSftp channelSftp) {
return new DefaultPooledObject<>(channelSftp);
}
// 銷毀對象
@Override
public void destroyObject(PooledObject<ChannelSftp> p) {
ChannelSftp channelSftp = p.getObject();
channelSftp.disconnect();
}
}
SftpPool.java
package com.vipsoft.sftp.pool;
import com.jcraft.jsch.ChannelSftp;
import org.apache.commons.pool2.impl.GenericObjectPool;
public class SftpPool<T> extends GenericObjectPool<ChannelSftp> {
public SftpPool(SftpFactory factory) {
super(factory,factory.getProperties().getPool());
}
/**
* 獲取一個sftp連接對象
* @return sftp連接對象
*/
@Override
public ChannelSftp borrowObject() throws Exception {
return super.borrowObject();
}
/**
* 歸還一個sftp連接對象
* @param channelSftp sftp連接對象
*/
@Override
public void returnObject(ChannelSftp channelSftp) {
if (channelSftp!=null) {
super.returnObject(channelSftp);
}
}
}
Utils
ByteUtil.java
package com.vipsoft.sftp.utils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
import com.vipsoft.sftp.exception.SftpPoolException;
import com.vipsoft.sftp.pool.SftpPool;
import java.io.InputStream;
public class SftpUtil {
private SftpPool pool;
public SftpUtil(SftpPool pool) {
this.pool = pool;
}
/**
* 下載文件
*
* @param dir 遠(yuǎn)程目錄
* @param name 遠(yuǎn)程文件名
* @return 文件字節(jié)數(shù)組
*/
public byte[] download(String dir, String name) {
ChannelSftp sftp = null;
try {
sftp = pool.borrowObject();
sftp.cd(dir);
InputStream in = sftp.get(name);
return ByteUtil.inputStreamToByteArray(in);
} catch (Exception e) {
throw new SftpPoolException("sftp下載文件出錯", e);
} finally {
pool.returnObject(sftp);
}
}
/**
* 上傳文件
*
* @param dir 遠(yuǎn)程目錄
* @param name 遠(yuǎn)程文件名
* @param in 輸入流
*/
public void upload(String dir, String name, InputStream in) {
ChannelSftp sftp = null;
try {
sftp = pool.borrowObject();
mkdirs(sftp, dir);
sftp.cd(dir);
sftp.put(in, name);
} catch (Exception e) {
throw new SftpPoolException("sftp上傳文件出錯", e);
} finally {
pool.returnObject(sftp);
}
}
/**
* 刪除文件
*
* @param dir 遠(yuǎn)程目錄
* @param name 遠(yuǎn)程文件名
*/
public void delete(String dir, String name) {
ChannelSftp sftp = null;
try {
sftp = pool.borrowObject();
sftp.cd(dir);
sftp.rm(name);
} catch (Exception e) {
throw new SftpPoolException("sftp刪除文件出錯", e);
} finally {
pool.returnObject(sftp);
}
}
/**
* 遞歸創(chuàng)建多級目錄
*
* @param dir 多級目錄
*/
private void mkdirs(ChannelSftp sftp, String dir) {
String[] folders = dir.split("/");
try {
sftp.cd("/");
for (String folder : folders) {
if (folder.length() > 0) {
try {
sftp.cd(folder);
} catch (Exception e) {
sftp.mkdir(folder);
sftp.cd(folder);
}
}
}
} catch (SftpException e) {
throw new SftpPoolException("sftp創(chuàng)建目錄出錯", e);
}
}
}
Test
SftpTest.java
package com.vipsoft.sftp;
import com.vipsoft.sftp.utils.SftpUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class SftpTest {
@Autowired
private SftpUtil sftpUtil;
@Test
void downloadTest() {
byte[] dockerfiles = sftpUtil.download("/opt/demo/", "Dockerfile");
System.out.println("FileSize =>" + dockerfiles.length);
}
}
到此這篇關(guān)于Java GenericObjectPool 對象池化技術(shù)--SpringBoot sftp 連接池工具類的文章就介紹到這了,更多相關(guān)Java 對象池GenericObjectPool內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
logback TimeBasedRollingPolicy按天生成日志源碼解析
這篇文章主要為大家介紹了logback TimeBasedRollingPolicy按天生成日志源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
SpringBoot整合Druid數(shù)據(jù)源的方法實現(xiàn)
Druid是阿里開發(fā)的一款開源的數(shù)據(jù)源,被很多人認(rèn)為是Java語言中最好的數(shù)據(jù)庫連接池,本文主要介紹了SpringBoot整合Druid數(shù)據(jù)源的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
JUC并發(fā)編程LinkedBlockingQueue隊列深入分析源碼
LinkedBlockingQueue 是一個可選有界阻塞隊列,這篇文章主要為大家詳細(xì)介紹了Java中LinkedBlockingQueue的實現(xiàn)原理與適用場景,感興趣的可以了解一下2023-04-04
springboot+thymeleaf+layui的實現(xiàn)示例
本文主要介紹了springboot+thymeleaf+layui的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12

