案例,
功能:
需要寫一個往kafka上報數據的組建。
當組建啟動時,需要建立如下資源:
1, 和kafka建立若干條連接
2, 啟動一個線程池
3, 啟動上報一個緩沖區
問題如下:
1, 如何在spring工程中引入該組件,並注入到spring容器中
2, 如間接被引用到此JAR包(如 引用的工程有引用到此組建JAR),或只是想用到里面數據類型,並不打算用功能時,如何避免資源會隨着引入而自行啟動造成資源浪費
3, 組建的配置如何統一管理問題
4, 如何管理眾多JAR包依賴,如, 此組建開發要用到kafka的0.11.0.2,有天需要升級到1.0.0
這些問題其實在spring cloud中都有比較好的解決方案,如 zuul, 后面也是仿造zuul的解決
一, 如何使該組建被spring工程引入
SPI方式,
如 zuul 的通過
\META-INF\spring.factories
進行引入 ,指定引導目錄
故 組建也定義如下
\META-INF\spring.factories
在SecurityAutoConfiguration中,根據需要對bean進行初始化,和相關資源的啟動。如 啟動連接,啟動本地線程池等。
但這里的問題是,只要引入了這個JAR包(包括間接引入該JAR包),那么所有工程都會平白無故的去連kafka,去啟動一些無用的線程池
二, 如何屏蔽間接被引用到此JAR包的工程啟動相關資源
解決這個問題,spring cloud和spring boot還稍有不同,先看spring cloud.
Marker方式
spring cloud標簽模式
先看zuul是怎么做的, 如要在工程里啟動ZUUL,一般會在main類里加入@EnableZuulProxy 標簽,如下:
需要引入@EnableZuulProxy 標簽
@EnableZuulProxy的源碼如下 : @EnableCircuitBreaker @EnableDiscoveryClient @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(ZuulProxyMarkerConfiguration.class) public @interface EnableZuulProxy { }
看到 @Import(ZuulProxyMarkerConfiguration.class), ZuulProxyMarkerConfiguration只做了一件事,引入一個maker標簽
如下:
這個Maker對象用作是否啟動啟用該配置,從而控制了資源是否啟動,如 ZuulServerAutoConfiguration
根據是否有Marker進行相關類的注入,是否啟動。
故:
此處,案例中的組件也選用了這種方式,如
@EnableZASecurityReport 標簽
當需要啟動時,在main類里加入標簽即可,如
后續kafka的連接類,線程池,緩沖區等是否分配都可以根據相關標識進行管理,如spi入口類SecurityAutoConfiguration :
這樣,如果在main啟動類中,只要未加入@EnableZASecurityReport,那么即使引入了組件的JAR包,相關資源也不會被啟動。
starter方式
還有一種方式,即,spring boot用的比較多的start方式
spring boot的所有配置都在spring-boot-autoconfigure/META-INF/spring.factories里,通過@ConditionalOnBean特定類是否引入來判斷是否啟動資源。
如: spring-boot-starter-data-redis
首先通過spring.factories,引入Redis的引導類
如:
RedisAutoConfiguration
當工程需要用到Redis時,通過Maven引入相關類
spring-boot-starter-data-redis, 其實是一個空項目,有個spring.provides,通過它引入redis相關的JAR包,然后使@ConditionalOnClass生效,從而完成對Redis的JAR環境的初始化。
三, 配置如何統一管理問題
一個組件(JAR包)出來后,配置會比較多,比如該項目涉及到kafka配置,線程池配置等一大堆,傳統方式是去寫個相關說明文檔,一堆配置項會讓使用起來很是麻煩。
1, 約定優於配置
在spring cloud/boot中,最讓人好用的就是 約定優於配置。記住約定,少寫配置
如在zuul中, 只需要配置幾個必須項,其它都是約定項
如,約定配置文件即application.yml( 或 bootstrap.yml):
簡單配置下routes就可以啟動,如果要找到zuul的配置的約定值,可以直接尋找總配置類ZuulProperties,
ZuulProperties里,包含了所有配置項,並通過配置對象的方式進行模塊話的划分如:
ZuulRoute相關,Host相關,HystrixSemaphore相關等
(也是一種默認約定)
故,
在組件中,也可以模仿簡化下配置。 此組件核心功能就是上報,比配項目應該只是kafka的地址,要啟用,只需要
report.kafkaConfig.servers=X.X.X.X:9092
即可,若要詳細配置,和約定值,用一個統一配置文件管 ReportProperties.java
里面注明約定配置的值
2,運用spring的自動裝配功能
運用@ConfigurationProperties標簽進行自動裝配。這個所有基本功能不細說。
詳細可查看ZuulProperties里。
好處在於:
1, 可以實現動態配置,如 配置 map,list,甚至enums等
2,如果配合spring cloud config,可以實現動態熱更新
四,統一管理JAR包的依賴
參考spring cloud/boot 里,JAR文件統一在spring-boot-dependencies的項目里單獨管理,而版本間的兼容,依靠了開源項目http://platform.spring.io/platform/ 來做管理,故很少存在版本沖突。
作為自研的組件,最好依賴到的第三方jar都由spring boot去同理管理版本號,而需要用到的其它jar,可用建立個dependencies項目單獨管理起來,不再自己工程能寫版本號,方便統一升級維護。
總結下:
1, 如何給spring /spring boot 項目提供組件會比較好
用SPI方式,方便平滑引用
2,如何避免不需要用到組件的項目誤引用JAR后,自動啟動組件相關資源
1, 提供@EnableXXX標簽模式,注入一個marker標簽,在啟動時通過@ConditionalOnBean來判斷
2,starter方式,配置與類分開,@ConditionalOnBean來判斷,同時引用時才啟動會
3,組件的配置如何統一管理
1, 約定大於配置,簡化配置。 為每個組件統一一個組件的XXXProperties.java,並提供約定值
2,自動裝配模式
4, 如何統一管理JAR包,防止JAR版本沖突等
交給spring boot統一管理,其它版本號統一在父工程(或加入dependencies工程) 管理版本
加個廣告,新的一年,打算把公眾號維護起來,質量做起來。
歡迎關注下,謝謝