最詳細的自定義Spring Boot Starter開發教程

碼農小胖哥發表於2019-08-19

1. 前言

隨著Spring的日漸臃腫,為了簡化配置、開箱即用、快速整合,Spring Boot 橫空出世。 目前已經成為 Java 目前最火熱的框架了。平常我們用Spring Boot開發web應用。Spring mvc 預設使用tomcat servlet容器, 因為Spring mvc元件整合了spring-boot-starter-tomcat 。但是現在undertow servlet容器的效能非常好。我們可以通過以下方式先排除tomcat:

然後直接替換為undertow:

程式碼無需更改。這就是元件化後的好處:1.可插拔。2.可定製。3.按需整合。為什麼能夠做到快速適配?我們試想一個這樣一個場景:假如你的汽車輪子上有個螺絲壞了,你要買一個螺絲去自己裝。你去店裡只要報上你汽車的品牌和位置老闆就能準確地知道你要用哪種螺絲。這就是標準已經制定好的好處。如果沒有標準,你很容易買到不配套的螺絲,你要不停的試錯。這顯然不是你想要的。 如果把這種標準潛移默化,那麼我們在溝通上就更加快捷方便。有時候你女朋友一個眼神你就知道她想要幹什麼。所以Spring Boot 有一個“約定大於配置”的規則,讓程式元件之間來減少配置,降低複雜性。因此你在開發一個自定義的Spring Boot Starter的時候也最好考慮你的starter如何達到以上的便利性。

2. Spring Boot的一些約定

一個元件的設計一定要有標準和規則。Spring Boot Starter也不例外。我們來看看一些常規的做法。

2.1 命名風格

如果你快有孩子了,出生前你比較急的一定是起個名字。姓名標識著你和你愛人的血統,一定不會起隔壁老王的姓氏,肯定會招來異樣的眼光。在maven中,groupId代表著姓氏,artifactId代表著名字。Spring Boot也是有一個命名的建議的。groupId不要用官方的org.springframework.boot
而要用你自己獨特的。對於artifactId的命名,Spring Boot官方建議非官方的Starter命名格式遵循 xxxx-spring-boot-starter ,例如 mybatis-spring-boot-starter 。官方starter會遵循spring-boot-starter-xxxx ,例如上面提到的spring-boot-starter-undertow 。很多開源starter作者會忽略這種約定,顯得不夠“專業“。

3. 自定義一個Starter

接下來我們構建一個自定義的第三方簡訊starter,命名為sms-spring-boot-starter 。有一些細節問題需要邊寫邊來介紹。下面是一個省略了samples和test模組模版:

依據上面我們建立如下專案:

3.1 sms-spring-boot

sms-spring-boot構建一個專案重要的就是依賴管理。所以引入BOM是必要的。主要管理該starter的所有模組module,以及starter的所有依賴甚至sms-spring-boot-autoconfigure都由sms-spring-boot管理。

3.2 sms-spring-boot-autoconfigure

該模組主要用來定義配置引數、以及自動配置對外暴露的功能(一般是抽象的介面Spring Bean)。

3.2.1 配置引數

一般配置引數都是在Spring Boot 的application.yml中。我們會定義一個字首標識來作為名稱空間隔離各個元件的引數。對應的元件會定義一個XXXXProperties 來自動裝配這些引數。自動裝配的機制基於@ConfigurationProperties註解,請注意一定要顯式宣告你配置的字首標識(prefix)。我們的sms-spring-boot會作如下配置:

c997518d5e8dba9a9bb69dd23a0770d8.png

以上以阿里雲的簡訊功能為例作配置,在將來使用時只需要在application.yml中加入上面對應SmsProperties的配置:

302dd03f038782022ec8cf43168620ca.png

如果你整合了Spring Boot 校驗庫 你也可以對SmsProperties進行校驗。在配置application.yml時細心的java開發者會發現引數配置都有像下面一樣的引數描述:
8092aac794d4b77ca959909c3af789dd.png

就像java中的註釋一樣方便我們理解該配置的作用,其實這個就是java註釋生成的。你需要依賴

8092aac794d4b77ca959909c3af789dd.png

然後就該依賴會對SmsProperties 成員屬性的註釋進行提取生成一個spring-configuration-metadata.json檔案,這就是配置描述的後設資料檔案。Spring Boot官方也對註釋進行了一些規則約束:

  • 不要以“The”或“A”開頭描述。
  • 對於boolean型別,請使用“Whether" 或“Enable”開始描述。
  • 對於基於集合的型別,請使用“Comma-separated list”
  • 如果預設時間單位不等同於毫秒,則使用java.time.Duration而不是long描述預設單位,例如“如果未指定持續時間字尾,則將使用秒”。
  • 除非必須在執行時確定,否則不要在描述中提供預設值。

補充我個人建議描述儘量使用英文描述。

3.2.2 配置自動暴露功能介面

拿到配置後,接下來就是根據配置來初始化我們的功能介面,我們會抽象一個簡訊傳送介面SmsSender,根據簡訊提供方的SDK來進行功能設計。請注意autoconfigure模組的依賴幾乎都是不可傳遞的。也就是依賴座標配置optional為true 。
功能介面實現完後我們會編寫一個自動配置類 SmsAutoConfiguration 。除了@Configuration註解外,@ConfigurationProperties會幫助我們將我們的配置類SmsProperties載入進來。然後將我們需要暴露的功能介面宣告為Spring Bean 暴露給Spring Boot應用 。有時候我們還可以通過一些條件來控制SmsAutoConfiguration或者SmsSender ,比如根據某個條件是否載入或載入不同的SmsSender。有時間你可以看看redis-starter就能很明顯感覺到,它會根據luttuce、redisson、jedis 的變化例項化不同的客戶端連結。實現方式是使用了@Conditional系列註解,有時間可以學習一下該系列的註解。好了我們的SmsAutoConfiguration宣告如下:

a9204a6d80a3df1a7e1a2ac7704c3664.png

3.2.3 主動生效和被動生效

starter整合入應用有兩種方式。我們從應用視角來看有兩種:

  • 一種是主動生效,在starter元件整合入Spring Boot應用時需要你主動宣告啟用該starter才生效,即使你配置完全。這裡會用到@Import註解,將該註解標記到你自定義的@Enable註解上:

be07d9a2aa637b267ae9cba39b8b6064.png
我們將該註解標記入Spring Boot應用就可以使用簡訊功能了。

  • 另一種被動生效,在starter元件整合入Spring Boot應用時就已經被應用捕捉到。這裡會用到類似java的SPI機制。在autoconfigure資源包下新建META-INF/spring.factories寫入SmsAutoConfiguration全限定名。

d3422497106918aadf5237c75f5d66ad.png

多個配置類逗號隔開,換行使用反斜槓。

3.3 sms-spring-boot-starter

該模組是一個空jar。它唯一目的是提供必要的依賴項來使用starter。你可以認為它就是整合該starter功能的唯一入口。不要對新增啟動器的專案做出假設。如果您自動配置的依賴庫通常需要其他啟動器,請同時提及它們。如果可選依賴項的數量很高,則提供一組適當的預設依賴項可能很難,因為您應該避免包含對典型庫的使用不必要的依賴項。換句話說,您不應該包含可選的依賴項。
無論哪種方式,您的starter必須直接或間接引用核心Spring Boot啟動器(spring-boot-starter)(如果您的啟動器依賴於另一個啟動器,則無需新增它)。如果只使用自定義啟動器建立專案,則Spring Boot的核心功能將通過核心啟動器的存在來實現。
我們的sms-spring-boot-starter僅僅是以下的pom:

5f45659e22f942fde9c31ee6103bac21.png

到此為止,我們的整個簡訊Starter就開發完成了。

4.總結

自定義starter對於我們專案元件化、模組化是有很大幫助的。同時也是Spring Boot一大特色。相信通過小胖的介紹你已經蠢蠢欲試了,那麼就趕緊開始寫一個吧。如果覺得對你有用可以點個贊關注一下。

關注公眾號:碼農小胖哥 獲取更多資訊

相關文章