摘要:封裝一個操作工具類概述前人的代碼中把操作和業務邏輯實現耦合在一起,據說經過多次的修改,在性能表現方面已經非常靠譜。連接對象池使用對象池管理方式需要提供一個工廠類,管理對象的生成銷毀等。
封裝一個FTP操作工具類 概述
前人的代碼中把FTP操作和業務邏輯實現耦合在一起,據說經過多次的修改,在性能表現方面已經非常靠譜。
在原來的代碼中可以看到使用了commons-net進行FTP操作,使用commons-pool對象池方式管理FTP連接,
完成了多線程下載和上傳的功能,本次的修改只是把耦合的地方剝離開來。
使用apache commons pool對象池管理方式需要提供一個工廠類,管理對象的生成銷毀等。
需要實現如下方法,
PooledObjectmakeObject(K key) throws Exception; void destroyObject(K key, PooledObject p) throws Exception; boolean validateObject(K key, PooledObject p);
apache commons pool提供了一個帶泛型的接口KeyedPooledObjectFactory
需要繼承實現類提供對象工廠的key類型,及要生產的對象類型,key可以是一個類,包含FTP的IP
,端口,用戶名密碼等屬性組成,目的是區分不同的FTP連接,
public class FtpClientConfig { private String host; private int port; private String username; private String password; ... }
這里要生產的對象類型當然是FTPClient了,所以我們的工廠類是這樣的,
public class FtpClientFactory implements KeyedPooledObjectFactory{ ... }
相應的,我們提供一個對象池的實現,其實很簡單
public class FtpClientPool extends GenericKeyedObjectPool{ public FtpClientPool(FtpClientFactory factory, FtpPoolConfig config) { super(factory, config); } }
構造方法的參數就是我們提供的對象工廠FtpClientFactory和FtpPoolConfig,FtpPoolConfig是
public class FtpPoolConfig extends GenericKeyedObjectPoolConfig{ public FtpPoolConfig() { setTestWhileIdle(true); setTimeBetweenEvictionRunsMillis(60000); setMinEvictableIdleTimeMillis(1800000L); setTestOnBorrow(true); } }
針對FTP連接設置了一些參數。
外部只要拿到FtpClientPool對象就可以獲取FTPClient對象、返回FTPClien對象了,
FTPClient的生成銷毀就交給了FtpClientFactory管理。
FTP連接池比方數據庫連接池來看,使用連接池似乎可以模仿Spring的JdbcTemplate,這個模板封裝了
獲取連接,執行數據庫操作,返還連接給連接池的過程,在這里同樣也適合。這里引入一個自己實現的"模板類",
public class FtpTemplate implements FtpOperations{ @Autowired private FtpClientPool ftpClientPool; }
繼承自己定義的FTP操作接口,并且注入上一步封裝好的對象池,當然在實踐過程中可能做不到像JdbcTemplate
那樣完全的泛型化。
比如為了泛型實例化,引入InterfaceConfig類我們才能真正實現FtpTemplate類。
public class FtpTemplate implements FtpOperations{ ... @Override public String getFile(InterfaceConfig k, String fileName) throws Exception { if (logger.isDebugEnabled()) { logger.debug("正在下載" + toFtpInfo(k) + "/" + fileName + "文件"); } final FTPClient client = getFtpClient(getFtpClientPool(), k); boolean ret = changeDirectory(client,k); try { if(ret) { return performPerFile(client, fileName); } return null; } catch(Exception e) { logger.error("下載" + toFtpInfo(k) + "/" + fileName + "文件異常",e); throw e; } finally { //return to object pool if(client != null) { returnFtpClient(getFtpClientPool(), k, client); } } } ... }
通過InterfaceConfig提供對象池識別的key獲得我們需要的對象池里的對象FTPClient。
private FTPClient getFtpClient(FtpClientPool ftpClientPool, InterfaceConfig k) throws Exception { FtpClientConfig config = buildFtpClientConfig(k); FTPClient client = null; try { client = ftpClientPool.borrowObject(config); } catch (Exception e) { logger.error("獲取FTPClient對象異常 " + toFtpInfo(k),e); throw e; } return client; } private FtpClientConfig buildFtpClientConfig(InterfaceConfig k) { FtpClientConfig config = new FtpClientConfig(); config.setHost(k.getFtpUrl()); config.setPort(Integer.valueOf(k.getFtpPort())); config.setUsername(k.getUserName()); config.setPassword(k.getPwd()); return config; }
這個InterfaceConfig是業務代碼中的對象類型,可能是Model類。目前為止我引入了一點點關于
業務的代碼,但還沒有耦合進來業務實現邏輯。
其實FtpTemplate已經是一個適合業務邏輯實現的工具類的,但是它的功能單純一些,為了完成特殊的業務功能,
如多線程下載,下載文件業務處理成功后才刪除遠端服務的文件等,這里再對FtpTemplate做一次封裝。
public class FtpUtils { @Autowired private FtpTemplate ftpTemplate; private ConcurrentHashMappoolMap = new ConcurrentHashMap<>(); //存儲線程池 public void downloadDirectory(InterfaceConfig config, final FtpCallback callback) { logger.info("正在下載FTP目錄" + toFtpInfo(config)); ThreadPoolExecutor workPool = poolMap.get(config.getInterfaceCode()); if(workPool == null) { BlockingQueue workQueue = new LinkedBlockingQueue (100); workPool = new ThreadPoolExecutor(MAX_CORE_NUM, MAX_THREAD_NUM, 1, TimeUnit.MINUTES, workQueue); poolMap.put(config.getInterfaceCode(), workPool); } try { List fileNames = ftpTemplate.listFiles(config,config.getOrdersCount()); BlockingQueue fileQueue = new LinkedBlockingQueue (fileNames); //生產者資料 for(int i = 0; i < config.getThreadNum(); i++) { try { workPool.execute(new GetFileConsumer(config, fileQueue, callback)); } catch (Exception e) { logger.error("提交線程出現異常",e); } } } catch (Exception e) { logger.error("FTP操作出現異常"+toFtpInfo(config),e); } logger.info("下載FTP目錄完成" + toFtpInfo(config)); } }
注入了FtpTemplate,加入了多線程線程池管理,下載方法也需要外部傳入回調方法。
回調方法中就可以完成保存下載的FTP文件,刪除遠端對應的文件等邏輯。即使了多了一層多線程
下載功能的封裝,我們也沒有把業務處理邏輯耦合進來。當然,不滿意的地方還是引入了業務的Model類。
略
程序調用圖 關于單元測試從上往下可以看出來三處封裝,分別是FtpUtils、FtpTemplate和FtpClientPool,我們可以分別
對他們進行單元測試,
注入FtpClientPool,測試FTP連接問題等
注入FtpTemplate,測試FTP操作問題等
注入FtpUtils,傳入回調函數,測試業務問題
由于JUnit對多線程單元測試并沒有提供支持,所以第3點實現起來有困難。
代碼地址https://github.com/Honwhy/com... 見master分支
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66241.html
摘要:寫在前面作為經常使用電腦整理文件的童鞋,應該都使用過從服務器上傳下載文件,那么今天就了解下如何通過程序操作服務的文件首先你要知道的,路徑,端口,有操作權限的賬號和密碼導入包這個包用來設置編碼,經過測試,不加也可用工具類中主要方法登陸驗證 寫在前面 作為經常使用電腦整理文件的童鞋,應該都使用過從ftp服務器上傳下載文件,那么今天就了解下如何通過java程序操作ftp服務的文件 首先你要知...
摘要:核心變化從第一版發布以來,社區發生了巨大變化。這意味著系統必須全部重寫。暫時不會為一個即將停止支持的版本發布新版本。路由路由功能將被更新。改進的日志系統日志系統將被改進,但具體細節尚未確定。第一階段第一階段將側重于抓住框架最重要的部分。 我們綜合考慮了社區的愿望和意見后,也對什么樣的未來對 CI 是最合適的做了一些思考,然后,CI 理事會對框架的未來做出了一些決策。預告一下,未來將會有...
摘要:沒想到會找到其他開發者針對又拍云開發又拍云管理工具這樣的工具,我個人覺得也算是又拍云在接口方面比較開放的一個的案例吧。 今年上半年,我通過又拍云搭建了一個獨立博客,不久之后就遇到了很多實際問題:網上看到圖片想收藏到空間,YouTube上的MV想放到自己的博客,想對一段音視頻進行在線預覽和編輯……當時我查了下,必須要通過API接口編寫一段程序才能完成(不是程序猿,搭建獨立博客已經要了我半...
閱讀 3226·2021-11-02 14:44
閱讀 3729·2021-09-02 15:41
閱讀 1671·2019-08-29 16:57
閱讀 1793·2019-08-26 13:38
閱讀 3302·2019-08-23 18:13
閱讀 2111·2019-08-23 15:41
閱讀 1677·2019-08-23 14:24
閱讀 3034·2019-08-23 14:03