咱們都知道可使用SpringBoot快速的開發基於Spring框架的項目。因爲圍繞SpringBoot存在不少開箱即用的Starter依賴,使得咱們在開發業務代碼時可以很是方便的、不須要過多關注框架的配置,而只須要關注業務便可。java
例如我想要在SpringBoot項目中集成Redis,那麼我只須要加入spring-data-redis-starter的依賴,並簡單配置一下鏈接信息以及Jedis鏈接池配置就能夠。這爲咱們省去了以前不少的配置操做。甚至有些功能的開啓只須要在啓動類或配置類上增長一個註解便可完成。web
那麼若是咱們想要本身實現本身的Starter須要作些什麼呢?下面就開始介紹如何實現本身的SpringBoot-xxx-starter。redis
首先說說原理,咱們知道使用一個公用的starter的時候,只須要將相應的依賴添加的Maven的配置文件當中便可,免去了本身須要引用不少依賴類,而且SpringBoot會自動進行類的自動配置。那麼 SpringBoot 是如何知道要實例化哪些類,並進行自動配置的呢? 下面簡單說一下。spring
首先,SpringBoot 在啓動時會去依賴的starter包中尋找 resources/META-INF/spring.factories
文件,而後根據文件中配置的Jar包去掃描項目所依賴的Jar包,這相似於 Java 的 SPI 機制。json
第二步,根據 spring.factories
配置加載AutoConfigure
類。springboot
最後,根據 @Conditional
註解的條件,進行自動配置並將Bean注入Spring Context 上下文當中。app
咱們也可使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class})
指定自動配置哪些類。框架
終於到了代碼實現的步驟,接下來就開始編碼咱們本身的SpringBoot-starter。spring-boot
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> </dependencies>
其中 spring-boot-configuration-processor
的做用是編譯時生成 spring-configuration-metadata.json
,此文件主要給IDE使用。如當配置此jar相關配置屬性在 application.yml
,你能夠用ctlr+鼠標左鍵點擊屬性名,IDE會跳轉到你配置此屬性的類中。測試
咱們平常使用的Spring官方的Starter通常採起spring-boot-starter-{name}
的命名方式,如 spring-boot-starter-web
。
而非官方的Starter,官方建議 artifactId
命名應遵循{name}-spring-boot-starter
的格式。 例如:ysc-spring-boot-starter
。
<groupId>com.ysc</groupId> <artifactId>simple-spring-boot-starter</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging>
這裏講一下咱們的Starter要實現的功能,很簡單,提供一個Service
,包含一個可以將配置文件中配置的字符串根據傳入的字符進行分割的方法String[] split(String separatorChar)
。
public class StarterService { private String config; public StarterService(String config) { this.config = config; } public String[] split(String separatorChar) { return StringUtils.split(this.config, separatorChar); } }
@ConfigurationProperties("example.service") public class StarterServiceProperties { private String config; public void setConfig(String config) { this.config = config; } public String getConfig() { return config; } }
AutoConfigure
類 ,這步是關鍵點@Configuration @ConditionalOnClass(StarterService.class) @EnableConfigurationProperties(StarterServiceProperties.class) public class StarterAutoConfigure { @Autowired private StarterServiceProperties properties; @Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "example.service", value = "enabled", havingValue = "true") StarterService starterService (){ return new StarterService(properties.getConfig()); } }
解釋一下代碼中用到的幾個註解:
@ConditionalOnClass
,當classpath
下發現該類的狀況下進行自動配置。@ConditionalOnMissingBean
,當Spring Context
中不存在該Bean
時。@ConditionalOnProperty(prefix = "example.service",value = "enabled",havingValue = "true")
,當配置文件中example.service.enabled=true
時。@ConditionalOnBean:當容器中有指定的Bean的條件下 @ConditionalOnClass:當類路徑下有指定的類的條件下 @ConditionalOnExpression:基於SpEL表達式做爲判斷條件 @ConditionalOnJava:基於JVM版本做爲判斷條件 @ConditionalOnJndi:在JNDI存在的條件下查找指定的位置 @ConditionalOnMissingBean:當容器中沒有指定Bean的狀況下 @ConditionalOnMissingClass:當類路徑下沒有指定的類的條件下 @ConditionalOnNotWebApplication:當前項目不是Web項目的條件下 @ConditionalOnProperty:指定的屬性是否有指定的值 @ConditionalOnResource:類路徑下是否有指定的資源 @ConditionalOnSingleCandidate:當指定的Bean在容器中只有一個,或者在有多個Bean的狀況下,用來指定首選的Bean @ConditionalOnWebApplication:當前項目是Web項目的條件下
resources/META-INF/
下建立spring.factories
文件,並添加以下內容:org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.autocinfigure.StarterAutoConfigure
至此,咱們的一個Starter代碼部分就是完成了,下面將項目安裝到本地Maven倉庫中。
在項目根目錄執行 mvn install
進行打包安裝。
將Starter項目的依賴添加到咱們本身的SpringBoot項目中
<dependency> <groupId>com.ysc</groupId> <artifactId>simple-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在application.yml
配置文件中添加配置信息:
example service enabled: true config: abc-des-dde,SSS-DRS-RE,SDR-SDFR-XXX
在本地使用JUnit
進行代碼測試
@Autowired private StarterService starterService; @Test public void starterTest() { String[] splitArray = starterService.split(","); System.out.println(splitArray); }
好,到這咱們的一個自定義Stater就完成了