摘要:提升自己,方便他人。其實一份文檔,說到底是為自己減輕工作量。總結(jié)總結(jié)起來,我們在提供一個通用包的時候,應(yīng)該考慮以下七個點文檔。支持的類反射獲取參數(shù)名面向接口編程。一個注解,快速整合
聲明:本文屬原創(chuàng)文章,首發(fā)于公號程序員自學(xué)之道,轉(zhuǎn)載請注明出處遇到槽點
開發(fā)實踐中,對于開發(fā)一個 jar 包,很多人都只是完成功能,只要功能使用沒問題,就算是完事了,但其實遠遠不夠。當用戶使用 jar 包的時候,可能會遇到以下這些問題:
文檔缺失,一個功能怎么用,往往需要花半天到一天的時候到處找負責人,一步步溝通,很浪費時間;
依賴沖突,我只是引用了一個用戶認證包,結(jié)果把它支持的 SpringMVC、Jersey 和 Struts2 全引進來了;
方法完全不知道參數(shù)名,一個有三個參數(shù)的接口,我得對著文檔才能知道它們分別是什么意思,沒有文檔就得找負責人溝通或者自己一個個猜了;
跟Spring整合很不友好,例如初始化配置強制要求文件全路徑;
因為經(jīng)常會遇到這樣的槽點,我在寫公共組件包的時候會特別留心。
在這里我總結(jié)出了以下七點改進建議,如果你也要提供 jar 包給其他人使用,可以參考。提升自己,方便他人。
文檔作為一個公共的 jar 包,很多項目可能會使用到,如果你沒有文檔,那么每次有人要用的時候就會找你各種詢問,這樣即浪費自己的時間也會浪費大家的時間。而且用的人越多,你會發(fā)現(xiàn),他們問的永遠都是那幾個問題:這個怎么用?你支持多種實現(xiàn)方式,我要選擇哪一種?如何申請使用?
如果你有一份簡單文檔就可以解決絕大多數(shù)的問題。
一份合格的文檔應(yīng)該包含如下內(nèi)容:
一句話描述本模塊的功能
快速開始,展示如何最簡單地開始使用
注意事項及常見問題
負責人聯(lián)系方式
一定要及時更新文檔,如果有文檔中沒有說明的問題,用戶找我們解決,記得要將這個解決方法記錄在常見問題中,為以后使用的人做參考。
其實一份文檔,說到底是為自己減輕工作量。試想,如果天天有人因為一些“雞毛蒜皮”的小事來各種問你,你又不得不花很多時間去溝通,有時溝通不好還會傷和氣。提供一份文檔,大家就都省事了。
最小依賴如無必要,勿引依賴。若有必要引入,但是并非必須,記得使用 provided。
例如,我們的 jar 包提供了快速整合 Spring 的功能,為此,我們需要添加 Spring 相關(guān)依賴,但是這個依賴是可選的,那么可以這樣設(shè)置:
org.springframework spring-context 5.1.8.RELEASE provided
加上 provided 意味著打包的時候不會將這個依賴加入到 jar 包中,而是需要使用者自己引入。
一個小小的設(shè)置,帶來的好處就是,如果這使用者不打算與 Spring 整合,那么他就不會間接地引入 Spring 的依賴了。這在一個大工程中相當重要,當一個項目中的外部依賴多了之后,外部依賴之間如果存在沖突,解決起來將會相當棘手。
附上源碼不知道你有沒有過這樣的經(jīng)歷:引用了一個 jar 包,準備開始使用的時候,代碼提示全是 var1, var2, var3 這種的,點進去一看,傻眼了:
這時 IDEA 還親切地問你,要不要下載源碼(Download Sources)看一下?你滿心期待了點了 Download!結(jié)果:
下載不了來問我要不要下載?玩我?
試想一下,這時你的用戶在用你的 jar 包的時候會不會也是這樣吐槽。那么怎么解決呢?
其實很簡單,只要在 pom 文件中添加 maven-source-plugin 插件即可。
org.apache.maven.plugins maven-source-plugin 3.0.1 attach-sources jar
這樣就可以在編譯時添加源碼包,當發(fā)布到maven倉庫時,也會自動帶上源碼。用戶在使用 IDEA 的時候也就可以直接下載并關(guān)聯(lián)源碼了。因為關(guān)聯(lián)上源碼,你寫在上面的注釋也可以被使用者看見,這可比文檔好用得多哦!
-parameters 參數(shù)Java8 的反射中添加了 Parameter 類,讓我們能在程序運行期間通過反射獲取到方法參數(shù)信息,包括參數(shù)名。但是需要在程序編譯的時候添加 -parameters 參數(shù)。做為一個 jar 包,如果我們在編譯的時候沒有加這個參數(shù),那么用戶將永遠無法通過反射獲取到參數(shù)名稱!這在某些場合下,可能會造成很大的不便。
其實,添加 -paramters 參數(shù)非常簡單,我們只需要在 pom 文件中添加 maven-compiler-plugin 插件,并且將 parameters 設(shè)置為 true 即可:
面向接口編程org.apache.maven.plugins maven-compiler-plugin 3.8.0 ${java.version} true UTF-8
做為一個公共 jar 包,我們是要對各個工程提供一個通用功能的,而這些功能一旦提供出去,需要保證兼容性,否則每次升級都將困難重重。
因此,我們應(yīng)該與使用者訂立“協(xié)議”,即通過接口訂立協(xié)議,宣告“我給大家提供這些能力,并且為之負責,你們無需關(guān)注我的底層實現(xiàn),只需要按照協(xié)議使用即可”。在接口注釋中注明使用的場景和注意事項,因為我們前面添加了源碼包,因此使用者可以直接關(guān)聯(lián)并查看到我們寫下的注釋,例如:
更極致的做法是我們只對接口負責。我們可以隱藏實現(xiàn)類(將實現(xiàn)類設(shè)置為包級私有的),然后通過工廠方法提供接口的實現(xiàn),而不是讓用戶自己 new。
這樣做之后,將來如果我們需要擴展,或者隨著技術(shù)的升級,我們需要更換底層實現(xiàn)時,無需擔心實現(xiàn)類中的兼容問題,只需要提供一個新的實現(xiàn)相同接口的實現(xiàn)類,讓工廠方法返回新的實現(xiàn)即可。而且舊的實現(xiàn)類,我們可以隨時刪除,減少歷史包袱!
包級私有的實現(xiàn)類:
每個 jar 包基本都會有自己的一些配置,這些配置如果初始化,也是有很多講究。我遇到最不靠譜的做法就是要求必須提供文件的絕對路徑,甚至有些是只支持默認絕對路徑不支持自定義!
因為遇到很多這樣奇葩的包,因此在寫 jar 包的時候都會特別留意。
總結(jié)起來,我們應(yīng)該提供如下三種配置的初始化方式:
文件路徑,必須支持 classpath: 前綴,代表從類路徑中加載
InputStream,支持從流中讀取
自定義的 Config 類,包含所有需要用到的配置項,并設(shè)置默認值
其中第三種,自定義的 Config 類,是最推薦的做法。
以上面的客戶端為例,我們可以提供這樣三個構(gòu)造器:
RocketMqEventClient(Config config) { this.config = config; client = new RocketMqClient(); } RocketMqEventClient(InputStream in) { init(in); } RocketMqEventClient(String filePath) { if (filePath == null || filePath.trim().isEmpty()) { throw new IllegalArgumentException("文件路徑不能為空"); } if (filePath.startsWith(CLASSPATH)) { // 從類路徑中加載 String path = filePath.replaceFirst(CLASSPATH, ""); try (InputStream in = RocketMqEventClient.class.getClassLoader().getResourceAsStream(path)) { init(in); } catch (IOException e) { throw new IllegalArgumentException("配置文件讀取失敗: " + filePath, e); } } else { // 直接讀取文件路徑 try (InputStream in = new FileInputStream(filePath)) { init(in); } catch (IOException e) { throw new IllegalArgumentException("配置文件讀取失敗: " + filePath, e); } } } private void init(InputStream in) { config = new Config(in); client = new RocketMqClient(); }
然后在工廠類中支持這幾種參數(shù)類型:
/** * 事件客戶端工廠 * * @author huangxuyang * @since 2019-06-29 */ public class EventClientFactory { /** * 創(chuàng)建默認的事件客戶端 * * @param config 各個配置項 * @return 默認的事件客戶端 */ public static EventClient createClient(Config config) { return new RocketMqEventClient(config); } /** * 創(chuàng)建默認的事件客戶端 * * @param in 配置文件輸入流 * @return 默認的事件客戶端 */ public static EventClient createClient(InputStream in) { return new RocketMqEventClient(in); } /** * 創(chuàng)建默認的事件客戶端 * * @param filePath 配置文件路徑,支持 classpath: 前綴 * @return 默認的事件客戶端 */ public static EventClient createClient(String filePath) { return new RocketMqEventClient(filePath); } }支持 Spring @Enable 模式
隨著 SpringBoot 越來越流行,starter 這種配置方式讓我們感受到原來整合第三方依賴可以這么方便。如果我們的 jar 包也支持 starter 肯定很酷。但是我一般會考慮到很多項目不是使用 SpringBoot 構(gòu)建,而是傳統(tǒng)的 Spring 項目,為了兼顧這些項目,其實我們可以采用 @EnableXxx 的模式,它與 starter 之間只是多了一個注解。我們只需要這么做:
引入 spring-context 依賴,注意加上 provided
在我們自定義的 Config 類的字段上使用 @Value 注解,自動從 Spring 上下文注入配置項
增加 XxxConfiguration 類,注冊 Bean
增加 @EnableXxx 注解,并導(dǎo)入剛剛定義的配置類 @Import({Config.class, XxxConfiguration.class})
以前面的事件客戶端為例,可以這樣做:
引入 spring-context
org.springframework spring-context 5.1.8.RELEASE provided
為 Config 配置類的字段添加 @Value 注解
@lombok.Data public class Config { @Value("${event.mq.namesrvaddr}") private String rocketMqNameSrvAddr; @Value("${event.mq.clientName}") private String rocketMqClientName; @Value("${event.mq.subject}") private String subject; @Value("${event.mq.pool.maxSize}") private int maxPoolSize; }
添加 EventClientConfiguration 類
/** * 事件客戶端自動裝配配置類 * * @author dadiyang * @since 2019-06-29 */ @Configuration public class EventClientConfiguration { @Bean public EventClient eventClient(Config config) { return EventClientFactory.createClient(config); } }
添加 @EnableEventClient 注解
/** * 啟用事件客戶端模塊 * * @author dadiyang * @since 2019-06-29 */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import({Config.class, EventClientConfiguration.class}) public @interface EnableEventClient { }
有了這個注解之后,使用者如果與 Spring 整合的話,只需要在帶有 @Configuration 注解的類上標注 @EnableEventClient,然后就可以 @Autowired 自動注入我們的 EventClient 類了!
如果團隊全部都使用 SpringBoot 進行開發(fā),也可以提供一個 starter。
總結(jié)總結(jié)起來,我們在提供一個通用 jar 包的時候,應(yīng)該考慮以下七個點:
文檔。節(jié)省溝通成本,你好我也好;
最小依賴。如無必要勿加依賴,可選依賴添加 provided;
附上源碼。代碼本身就是最好的文檔,加上 maven-source-plugin 就搞定;
編譯時加上 -parameters 參數(shù)。支持 Java8 的Parameter類反射獲取參數(shù)名;
面向接口編程。對且只對契約負責,隱藏實現(xiàn),方便將來更換實現(xiàn)又保持兼容性;
多種配置傳入方式。讓使用者以最方便的方式提供配置;
支持 Spring @Enable 模式。一個注解,快速整合 Spring;
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75079.html
摘要:而道器相融,在我看來,那煉丹就需要一個好的丹爐了,也就是一個優(yōu)秀的機器學(xué)習(xí)平臺。因此,一個機器學(xué)習(xí)平臺要取得成功,最好具備如下五個特點精辟的核心抽象一個機器學(xué)習(xí)平臺,必須有其靈魂,也就是它的核心抽象。 *本文首發(fā)于 AI前線 ,歡迎轉(zhuǎn)載,并請注明出處。 摘要 2017年6月,騰訊正式開源面向機器學(xué)習(xí)的第三代高性能計算平臺 Angel,在GitHub上備受關(guān)注;2017年10月19日,騰...
摘要:自由職業(yè)是一個令人向往的的職業(yè)。為了努力成為一個成功的自由職業(yè)者,犧牲了無數(shù)的休息時間,導(dǎo)致了身體過早的出現(xiàn)問題。 自由職業(yè)是一個令人向往的的職業(yè)。 如何成為自由職業(yè)者?自由職業(yè)的種類有很多, 自由撰稿人,網(wǎng)絡(luò)作家, 自媒體,微商, 淘寶店, 個人站長, 懂技術(shù)的還可以自己接活,豬八戒上面有很多這種任務(wù), 開源中國還開發(fā)了一個眾包平臺, 如果英語過硬的話還可以上國外的網(wǎng)站接活,有名的f...
摘要:任何數(shù)據(jù)結(jié)構(gòu)只要部署接口,就可以完成遍歷操作即依次處理該數(shù)據(jù)結(jié)構(gòu)的成員。的遍歷某個數(shù)據(jù)結(jié)構(gòu)過程是這樣的比如對進行遍歷創(chuàng)建一個指針對象,指向當前數(shù)組的起始位置。 Iterator 這真是毅種循環(huán) Iterator不是array,也不是set,不是map, 它不是一個實體,而是一種訪問機制,是一個用來訪問某個對象的接口規(guī)范,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制。任何數(shù)據(jù)結(jié)構(gòu)只要部署Ite...
摘要:任何數(shù)據(jù)結(jié)構(gòu)只要部署接口,就可以完成遍歷操作即依次處理該數(shù)據(jù)結(jié)構(gòu)的成員。的遍歷某個數(shù)據(jù)結(jié)構(gòu)過程是這樣的比如對進行遍歷創(chuàng)建一個指針對象,指向當前數(shù)組的起始位置。 Iterator 這真是毅種循環(huán) Iterator不是array,也不是set,不是map, 它不是一個實體,而是一種訪問機制,是一個用來訪問某個對象的接口規(guī)范,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制。任何數(shù)據(jù)結(jié)構(gòu)只要部署Ite...
閱讀 2721·2023-04-25 21:26
閱讀 1521·2021-11-25 09:43
閱讀 1956·2019-08-30 15:52
閱讀 937·2019-08-30 14:05
閱讀 2622·2019-08-29 16:10
閱讀 421·2019-08-29 13:48
閱讀 1867·2019-08-29 12:47
閱讀 1306·2019-08-23 18:04