案例,java
功能:web
須要寫一個往kafka上報數據的組建。redis
當組建啓動時,須要創建以下資源:spring
1, 和kafka創建若干條鏈接bootstrap
2, 啓動一個線程池app
3, 啓動上報一個緩衝區ide
1, 如何在spring工程中引入該組件,並注入到spring容器中spring-boot
2, 如間接被引用到此JAR包(如 引用的工程有引用到此組建JAR),或只是想用到裏面數據類型,並不打算用功能時,如何避免資源會隨着引入而自行啓動形成資源浪費ui
3, 組建的配置如何統一管理問題spa
4, 如何管理衆多JAR包依賴,如, 此組建開發要用到kafka的0.11.0.2,有天須要升級到1.0.0
這些問題其實在spring cloud中都有比較好的解決方案,如 zuul, 後面也是仿造zuul的解決
SPI方式,
如 zuul 的經過
\META-INF\spring.factories
進行引入 ,指定引導目錄
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\ org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration
故 組建也定義以下
\META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.zhenai.security.report.SecurityAutoConfiguration
在SecurityAutoConfiguration中,根據須要對bean進行初始化,和相關資源的啓動。如 啓動鏈接,啓動本地線程池等。
但這裏的問題是,只要引入了這個JAR包(包括間接引入該JAR包),那麼全部工程都會無緣無故的去連kafka,去啓動一些無用的線程池
解決這個問題,spring cloud和spring boot還稍有不一樣,先看spring cloud.
spring cloud標籤模式
先看zuul是怎麼作的, 如要在工程裏啓動ZUUL,通常會在main類里加入@EnableZuulProxy 標籤,以下:
@SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class Application { ... .... }
須要引入@EnableZuulProxy 標籤
@EnableZuulProxy的源碼以下 :
@EnableCircuitBreaker @EnableDiscoveryClient @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(ZuulProxyMarkerConfiguration.class) public @interface EnableZuulProxy { }
看到 @Import(ZuulProxyMarkerConfiguration.class), ZuulProxyMarkerConfiguration只作了一件事,引入一個maker標籤
以下:
@Configuration public class ZuulProxyMarkerConfiguration { @Bean public Marker zuulProxyMarkerBean() { return new Marker(); } class Marker { } }
這個Maker對象用做是否啓動啓用該配置,從而控制了資源是否啓動,如 ZuulServerAutoConfiguration
@Configuration @EnableConfigurationProperties({ ZuulProperties.class }) @ConditionalOnClass(ZuulServlet.class) @ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class) // Make sure to get the ServerProperties from the same place as a normal web app would @Import(ServerPropertiesAutoConfiguration.class) public class ZuulServerAutoConfiguration { ... ... }
根據是否有Marker進行相關類的注入,是否啓動。
故:
此處,案例中的組件也選用了這種方式,如
@EnableZASecurityReport 標籤
當須要啓動時,在main類里加入標籤便可,如
@EnableZASecurityReport public class Application { ... .... }
後續kafka的鏈接類,線程池,緩衝區等是否分配均可以根據相關標識進行管理,如spi入口類SecurityAutoConfiguration :
@Configuration @EnableConfigurationProperties({ SecurityReportProperties.class }) @ConditionalOnBean(SecurityProxyMarkerConfiguration.Marker.class) public class SecurityAutoConfiguration { ... ... }
這樣,若是在main啓動類中,只要未加入@EnableZASecurityReport,那麼即便引入了組件的JAR包,相關資源也不會被啓動。
還有一種方式,即,spring boot用的比較多的start方式
spring boot的全部配置都在spring-boot-autoconfigure/META-INF/spring.factories裏,經過@ConditionalOnBean特定類是否引入來判斷是否啓動資源。
如: spring-boot-starter-data-redis
首先經過spring.factories,引入Redis的引導類
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
如:
RedisAutoConfiguration
@Configuration @ConditionalOnClass({ RedisOperations.class }) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { ... ... }
當工程須要用到Redis時,經過Maven引入相關類
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency>
spring-boot-starter-data-redis, 實際上是一個空項目,有個spring.provides,經過它引入redis相關的JAR包,而後使@ConditionalOnClass生效,從而完成對Redis的JAR環境的初始化。
一個組件(JAR包)出來後,配置會比較多,好比該項目涉及到kafka配置,線程池配置等一大堆,傳統方式是去寫個相關說明文檔,一堆配置項會讓使用起來非常麻煩。
在spring cloud/boot中,最讓人好用的就是 約定優於配置。記住約定,少寫配置
如在zuul中, 只須要配置幾個必須項,其它都是約定項
如,約定配置文件即application.yml( 或 bootstrap.yml):
zuul: debug: routes: ZHENAI-CLIENT: path: /test/**
簡單配置下routes就能夠啓動,若是要找到zuul的配置的約定值,能夠直接尋找總配置類ZuulProperties,
ZuulProperties裏,包含了全部配置項,並經過配置對象的方式進行模塊話的劃分如:
ZuulRoute相關,Host相關,HystrixSemaphore相關等
(也是一種默認約定)
故,
在組件中,也能夠模仿簡化下配置。 此組件核心功能就是上報,比配項目應該只是kafka的地址,要啓用,只須要
report.kafkaConfig.servers=X.X.X.X:9092
便可,若要詳細配置,和約定值,用一個統一配置文件管 ReportProperties.java
裏面註明約定配置的值
運用@ConfigurationProperties標籤進行自動裝配。這個全部基本功能不細說。
詳細可查看ZuulProperties裏。
好處在於:
1, 能夠實現動態配置,如 配置 map,list,甚至enums等
2,若是配合spring cloud config,能夠實現動態熱更新
參考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工程) 管理版本
加個廣告,新的一年,打算把公衆號維護起來,質量作起來。
歡迎關注下,謝謝