前言
我們都知道可以使用SpringBoot快速的開發基於Spring框架的項目。由於圍繞SpringBoot存在很多開箱即用的Starter依賴,使得我們在開發業務代碼時能夠非常方便的、不需要過多關注框架的配置,而只需要關注業務即可。
例如我想要在SpringBoot項目中集成Redis,那么我只需要加入spring-data-redis-starter的依賴,並簡單配置一下連接信息以及Jedis連接池配置就可以。這為我們省去了之前很多的配置操作。甚至有些功能的開啟只需要在啟動類或配置類上增加一個注解即可完成。
那么如果我們想要自己實現自己的Starter需要做些什么呢?下面就開始介紹如何實現自己的SpringBoot-xxx-starter。
原理
首先說說原理,我們知道使用一個公用的starter的時候,只需要將相應的依賴添加的Maven的配置文件當中即可,免去了自己需要引用很多依賴類,並且SpringBoot會自動進行類的自動配置。那么 SpringBoot 是如何知道要實例化哪些類,並進行自動配置的呢? 下面簡單說一下。
首先,SpringBoot 在啟動時會去依賴的starter包中尋找 resources/META-INF/spring.factories 文件,然后根據文件中配置的Jar包去掃描項目所依賴的Jar包,這類似於 Java 的 SPI 機制。
第二步,根據 spring.factories 配置加載AutoConfigure類。
最后,根據 @Conditional 注解的條件,進行自動配置並將Bean注入Spring Context 上下文當中。
我們也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class}) 指定自動配置哪些類。
實現
終於到了代碼實現的步驟,接下來就開始編碼我們自己的SpringBoot-starter。
第一步創建一個SpringBoot 項目,並添加下面兩個依賴到pom.xml文件當中
<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>
第二步編寫我們的Service類
這里講一下我們的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時。
下面列舉SpringBoot中的所有@Conditional注解及作用
@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就完成了

