成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【極致】一個(gè) jar 包的自我修養(yǎng)

pumpkin9 / 1506人閱讀

摘要:提升自己,方便他人。其實(shí)一份文檔,說(shuō)到底是為自己減輕工作量。總結(jié)總結(jié)起來(lái),我們?cè)谔峁┮粋€(gè)通用包的時(shí)候,應(yīng)該考慮以下七個(gè)點(diǎn)文檔。支持的類反射獲取參數(shù)名面向接口編程。一個(gè)注解,快速整合

聲明:本文屬原創(chuàng)文章,首發(fā)于公號(hào)程序員自學(xué)之道,轉(zhuǎn)載請(qǐng)注明出處
遇到槽點(diǎn)

開(kāi)發(fā)實(shí)踐中,對(duì)于開(kāi)發(fā)一個(gè) jar 包,很多人都只是完成功能,只要功能使用沒(méi)問(wèn)題,就算是完事了,但其實(shí)遠(yuǎn)遠(yuǎn)不夠。當(dāng)用戶使用 jar 包的時(shí)候,可能會(huì)遇到以下這些問(wèn)題:

文檔缺失,一個(gè)功能怎么用,往往需要花半天到一天的時(shí)候到處找負(fù)責(zé)人,一步步溝通,很浪費(fèi)時(shí)間;

依賴沖突,我只是引用了一個(gè)用戶認(rèn)證包,結(jié)果把它支持的 SpringMVC、Jersey 和 Struts2 全引進(jìn)來(lái)了;

方法完全不知道參數(shù)名,一個(gè)有三個(gè)參數(shù)的接口,我得對(duì)著文檔才能知道它們分別是什么意思,沒(méi)有文檔就得找負(fù)責(zé)人溝通或者自己一個(gè)個(gè)猜了;

跟Spring整合很不友好,例如初始化配置強(qiáng)制要求文件全路徑;

因?yàn)榻?jīng)常會(huì)遇到這樣的槽點(diǎn),我在寫(xiě)公共組件包的時(shí)候會(huì)特別留心。

在這里我總結(jié)出了以下七點(diǎn)改進(jìn)建議,如果你也要提供 jar 包給其他人使用,可以參考。提升自己,方便他人。

文檔

作為一個(gè)公共的 jar 包,很多項(xiàng)目可能會(huì)使用到,如果你沒(méi)有文檔,那么每次有人要用的時(shí)候就會(huì)找你各種詢問(wèn),這樣即浪費(fèi)自己的時(shí)間也會(huì)浪費(fèi)大家的時(shí)間。而且用的人越多,你會(huì)發(fā)現(xiàn),他們問(wèn)的永遠(yuǎn)都是那幾個(gè)問(wèn)題:這個(gè)怎么用?你支持多種實(shí)現(xiàn)方式,我要選擇哪一種?如何申請(qǐng)使用?

如果你有一份簡(jiǎn)單文檔就可以解決絕大多數(shù)的問(wèn)題。

一份合格的文檔應(yīng)該包含如下內(nèi)容:

一句話描述本模塊的功能

快速開(kāi)始,展示如何最簡(jiǎn)單地開(kāi)始使用

注意事項(xiàng)及常見(jiàn)問(wèn)題

負(fù)責(zé)人聯(lián)系方式

一定要及時(shí)更新文檔,如果有文檔中沒(méi)有說(shuō)明的問(wèn)題,用戶找我們解決,記得要將這個(gè)解決方法記錄在常見(jiàn)問(wèn)題中,為以后使用的人做參考。

其實(shí)一份文檔,說(shuō)到底是為自己減輕工作量。試想,如果天天有人因?yàn)橐恍半u毛蒜皮”的小事來(lái)各種問(wèn)你,你又不得不花很多時(shí)間去溝通,有時(shí)溝通不好還會(huì)傷和氣。提供一份文檔,大家就都省事了。

最小依賴

如無(wú)必要,勿引依賴。若有必要引入,但是并非必須,記得使用 provided

例如,我們的 jar 包提供了快速整合 Spring 的功能,為此,我們需要添加 Spring 相關(guān)依賴,但是這個(gè)依賴是可選的,那么可以這樣設(shè)置:


    org.springframework
    spring-context
    5.1.8.RELEASE
    provided

加上 provided 意味著打包的時(shí)候不會(huì)將這個(gè)依賴加入到 jar 包中,而是需要使用者自己引入。

一個(gè)小小的設(shè)置,帶來(lái)的好處就是,如果這使用者不打算與 Spring 整合,那么他就不會(huì)間接地引入 Spring 的依賴了。這在一個(gè)大工程中相當(dāng)重要,當(dāng)一個(gè)項(xiàng)目中的外部依賴多了之后,外部依賴之間如果存在沖突,解決起來(lái)將會(huì)相當(dāng)棘手。

附上源碼

不知道你有沒(méi)有過(guò)這樣的經(jīng)歷:引用了一個(gè) jar 包,準(zhǔn)備開(kāi)始使用的時(shí)候,代碼提示全是 var1, var2, var3 這種的,點(diǎn)進(jìn)去一看,傻眼了:

這時(shí) IDEA 還親切地問(wèn)你,要不要下載源碼(Download Sources)看一下?你滿心期待了點(diǎn)了 Download!結(jié)果:

下載不了來(lái)問(wèn)我要不要下載?玩我?

試想一下,這時(shí)你的用戶在用你的 jar 包的時(shí)候會(huì)不會(huì)也是這樣吐槽。那么怎么解決呢?

其實(shí)很簡(jiǎn)單,只要在 pom 文件中添加 maven-source-plugin 插件即可



    org.apache.maven.plugins
    maven-source-plugin
    3.0.1
    
        
            attach-sources
            
                jar
            
        
    

這樣就可以在編譯時(shí)添加源碼包,當(dāng)發(fā)布到maven倉(cāng)庫(kù)時(shí),也會(huì)自動(dòng)帶上源碼。用戶在使用 IDEA 的時(shí)候也就可以直接下載并關(guān)聯(lián)源碼了。因?yàn)殛P(guān)聯(lián)上源碼,你寫(xiě)在上面的注釋也可以被使用者看見(jiàn),這可比文檔好用得多哦!

-parameters 參數(shù)

Java8 的反射中添加了 Parameter 類,讓我們能在程序運(yùn)行期間通過(guò)反射獲取到方法參數(shù)信息,包括參數(shù)名。但是需要在程序編譯的時(shí)候添加 -parameters 參數(shù)。做為一個(gè) jar 包,如果我們?cè)诰幾g的時(shí)候沒(méi)有加這個(gè)參數(shù),那么用戶將永遠(yuǎn)無(wú)法通過(guò)反射獲取到參數(shù)名稱!這在某些場(chǎng)合下,可能會(huì)造成很大的不便。

其實(shí),添加 -paramters 參數(shù)非常簡(jiǎn)單,我們只需要在 pom 文件中添加 maven-compiler-plugin 插件,并且將 parameters 設(shè)置為 true 即可:


    org.apache.maven.plugins
    maven-compiler-plugin
    3.8.0
    
        ${java.version}
        ${java.version}
        true
        UTF-8
    
面向接口編程

做為一個(gè)公共 jar 包,我們是要對(duì)各個(gè)工程提供一個(gè)通用功能的,而這些功能一旦提供出去,需要保證兼容性,否則每次升級(jí)都將困難重重。

因此,我們應(yīng)該與使用者訂立“協(xié)議”,即通過(guò)接口訂立協(xié)議,宣告“我給大家提供這些能力,并且為之負(fù)責(zé),你們無(wú)需關(guān)注我的底層實(shí)現(xiàn),只需要按照協(xié)議使用即可”。在接口注釋中注明使用的場(chǎng)景和注意事項(xiàng),因?yàn)槲覀兦懊嫣砑恿嗽创a包,因此使用者可以直接關(guān)聯(lián)并查看到我們寫(xiě)下的注釋,例如:

更極致的做法是我們只對(duì)接口負(fù)責(zé)我們可以隱藏實(shí)現(xiàn)類(將實(shí)現(xiàn)類設(shè)置為包級(jí)私有的),然后通過(guò)工廠方法提供接口的實(shí)現(xiàn),而不是讓用戶自己 new。

這樣做之后,將來(lái)如果我們需要擴(kuò)展,或者隨著技術(shù)的升級(jí),我們需要更換底層實(shí)現(xiàn)時(shí),無(wú)需擔(dān)心實(shí)現(xiàn)類中的兼容問(wèn)題,只需要提供一個(gè)新的實(shí)現(xiàn)相同接口的實(shí)現(xiàn)類,讓工廠方法返回新的實(shí)現(xiàn)即可。而且舊的實(shí)現(xiàn)類,我們可以隨時(shí)刪除,減少歷史包袱

包級(jí)私有的實(shí)現(xiàn)類:

多種配置傳入方式

每個(gè) jar 包基本都會(huì)有自己的一些配置,這些配置如果初始化,也是有很多講究。我遇到最不靠譜的做法就是要求必須提供文件的絕對(duì)路徑,甚至有些是只支持默認(rèn)絕對(duì)路徑不支持自定義!

因?yàn)橛龅胶芏噙@樣奇葩的包,因此在寫(xiě) jar 包的時(shí)候都會(huì)特別留意。

總結(jié)起來(lái),我們應(yīng)該提供如下三種配置的初始化方式:

文件路徑,必須支持 classpath: 前綴,代表從類路徑中加載

InputStream,支持從流中讀取

自定義的 Config 類,包含所有需要用到的配置項(xiàng),并設(shè)置默認(rèn)值

其中第三種,自定義的 Config 類,是最推薦的做法。

以上面的客戶端為例,我們可以提供這樣三個(gè)構(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)建默認(rèn)的事件客戶端
     *
     * @param config 各個(gè)配置項(xiàng)
     * @return 默認(rèn)的事件客戶端
     */
    public static EventClient createClient(Config config) {
        return new RocketMqEventClient(config);
    }

    /**
     * 創(chuàng)建默認(rèn)的事件客戶端
     *
     * @param in 配置文件輸入流
     * @return 默認(rèn)的事件客戶端
     */
    public static EventClient createClient(InputStream in) {
        return new RocketMqEventClient(in);
    }

    /**
     * 創(chuàng)建默認(rèn)的事件客戶端
     *
     * @param filePath 配置文件路徑,支持 classpath: 前綴
     * @return 默認(rèn)的事件客戶端
     */
    public static EventClient createClient(String filePath) {
        return new RocketMqEventClient(filePath);
    }
}
支持 Spring @Enable 模式

隨著 SpringBoot 越來(lái)越流行,starter 這種配置方式讓我們感受到原來(lái)整合第三方依賴可以這么方便。如果我們的 jar 包也支持 starter 肯定很酷。但是我一般會(huì)考慮到很多項(xiàng)目不是使用 SpringBoot 構(gòu)建,而是傳統(tǒng)的 Spring 項(xiàng)目,為了兼顧這些項(xiàng)目,其實(shí)我們可以采用 @EnableXxx 的模式,它與 starter 之間只是多了一個(gè)注解。我們只需要這么做:

引入 spring-context 依賴,注意加上 provided

在我們自定義的 Config 類的字段上使用 @Value 注解,自動(dòng)從 Spring 上下文注入配置項(xiàng)

增加 XxxConfiguration 類,注冊(cè) 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 類

/**
 * 事件客戶端自動(dòng)裝配配置類
 *
 * @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 {
 }

有了這個(gè)注解之后,使用者如果與 Spring 整合的話,只需要在帶有 @Configuration 注解的類上標(biāo)注 @EnableEventClient,然后就可以 @Autowired 自動(dòng)注入我們的 EventClient 類了!

如果團(tuán)隊(duì)全部都使用 SpringBoot 進(jìn)行開(kāi)發(fā),也可以提供一個(gè) starter。

總結(jié)

總結(jié)起來(lái),我們?cè)谔峁┮粋€(gè)通用 jar 包的時(shí)候,應(yīng)該考慮以下七個(gè)點(diǎn):

文檔。節(jié)省溝通成本,你好我也好;

最小依賴。如無(wú)必要勿加依賴,可選依賴添加 provided;

附上源碼。代碼本身就是最好的文檔,加上 maven-source-plugin 就搞定;

編譯時(shí)加上 -parameters 參數(shù)。支持 Java8 的Parameter類反射獲取參數(shù)名;

面向接口編程。對(duì)且只對(duì)契約負(fù)責(zé),隱藏實(shí)現(xiàn),方便將來(lái)更換實(shí)現(xiàn)又保持兼容性;

多種配置傳入方式。讓使用者以最方便的方式提供配置;

支持 Spring @Enable 模式。一個(gè)注解,快速整合 Spring;

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75079.html

相關(guān)文章

  • 道器相融,由Angel論一個(gè)優(yōu)秀機(jī)器學(xué)習(xí)平臺(tái)的自我修養(yǎng)

    摘要:而道器相融,在我看來(lái),那煉丹就需要一個(gè)好的丹爐了,也就是一個(gè)優(yōu)秀的機(jī)器學(xué)習(xí)平臺(tái)。因此,一個(gè)機(jī)器學(xué)習(xí)平臺(tái)要取得成功,最好具備如下五個(gè)特點(diǎn)精辟的核心抽象一個(gè)機(jī)器學(xué)習(xí)平臺(tái),必須有其靈魂,也就是它的核心抽象。 *本文首發(fā)于 AI前線 ,歡迎轉(zhuǎn)載,并請(qǐng)注明出處。 摘要 2017年6月,騰訊正式開(kāi)源面向機(jī)器學(xué)習(xí)的第三代高性能計(jì)算平臺(tái) Angel,在GitHub上備受關(guān)注;2017年10月19日,騰...

    leo108 評(píng)論0 收藏0
  • 自由職業(yè)者的自我修養(yǎng)

    摘要:自由職業(yè)是一個(gè)令人向往的的職業(yè)。為了努力成為一個(gè)成功的自由職業(yè)者,犧牲了無(wú)數(shù)的休息時(shí)間,導(dǎo)致了身體過(guò)早的出現(xiàn)問(wèn)題。 自由職業(yè)是一個(gè)令人向往的的職業(yè)。 如何成為自由職業(yè)者?自由職業(yè)的種類有很多, 自由撰稿人,網(wǎng)絡(luò)作家, 自媒體,微商, 淘寶店, 個(gè)人站長(zhǎng), 懂技術(shù)的還可以自己接活,豬八戒上面有很多這種任務(wù), 開(kāi)源中國(guó)還開(kāi)發(fā)了一個(gè)眾包平臺(tái), 如果英語(yǔ)過(guò)硬的話還可以上國(guó)外的網(wǎng)站接活,有名的f...

    binaryTree 評(píng)論0 收藏0
  • 切圖崽的自我修養(yǎng)-[ES6] 迭代器Iterator淺析

    摘要:任何數(shù)據(jù)結(jié)構(gòu)只要部署接口,就可以完成遍歷操作即依次處理該數(shù)據(jù)結(jié)構(gòu)的成員。的遍歷某個(gè)數(shù)據(jù)結(jié)構(gòu)過(guò)程是這樣的比如對(duì)進(jìn)行遍歷創(chuàng)建一個(gè)指針對(duì)象,指向當(dāng)前數(shù)組的起始位置。 Iterator 這真是毅種循環(huán) Iterator不是array,也不是set,不是map, 它不是一個(gè)實(shí)體,而是一種訪問(wèn)機(jī)制,是一個(gè)用來(lái)訪問(wèn)某個(gè)對(duì)象的接口規(guī)范,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問(wèn)機(jī)制。任何數(shù)據(jù)結(jié)構(gòu)只要部署Ite...

    neu 評(píng)論0 收藏0
  • 切圖崽的自我修養(yǎng)-[ES6] 迭代器Iterator淺析

    摘要:任何數(shù)據(jù)結(jié)構(gòu)只要部署接口,就可以完成遍歷操作即依次處理該數(shù)據(jù)結(jié)構(gòu)的成員。的遍歷某個(gè)數(shù)據(jù)結(jié)構(gòu)過(guò)程是這樣的比如對(duì)進(jìn)行遍歷創(chuàng)建一個(gè)指針對(duì)象,指向當(dāng)前數(shù)組的起始位置。 Iterator 這真是毅種循環(huán) Iterator不是array,也不是set,不是map, 它不是一個(gè)實(shí)體,而是一種訪問(wèn)機(jī)制,是一個(gè)用來(lái)訪問(wèn)某個(gè)對(duì)象的接口規(guī)范,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問(wèn)機(jī)制。任何數(shù)據(jù)結(jié)構(gòu)只要部署Ite...

    springDevBird 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

pumpkin9

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<