自己實現一個Starter
自己實現SpringBoot裏面的Starter
原理
一個公用是starter我們只需要引入pom文件,SpringBoot就會進行自動配置。那麼SpringBoot是如何知道要實例化哪些類,並進行簡單配置呢?
-
首先,SpringBoot在啓動時會去依賴的Starter包中尋找
resources/META-INF/spring.factories
文件,然後根據文件中配置的Jar包去掃描項目所依賴的Jar包,這類似於 Java 的 SPI 機制。 -
第二步,根據
spring.factories
配置加載AutoConfigure
類 -
最後,根據
@Conditional
註解的條件,進行自動配置並將Bean注入Spring Context 上下文當中。我們也可以使用
@ImportAutoConfiguration({MyServiceAutoConfiguration.class})
指定自動配置哪些類。
可以認爲starter是一種服務——使得使用某個功能的開發者不需要關注各種依賴庫的處理,不需要具體的配置信息, 由Spring Boot自動通過classpath路徑下的類發現需要的Bean,並織入相應的Bean。舉個栗子,spring-boot-starter-jdbc這個starter的存在, 使得我們只需要在BookPubApplication下用@Autowired引入DataSource的bean就可以,Spring Boot會自動創建DataSource的實例。
核心知識
1. @Conditional註解
核心就是條件註解 @Conditional
,使用方式如下
//當項目當前Classpath存在 HelloService的時候 後面的配置才生效
@ConditionalOnClass(HelloService.class)
public class HelloServiceAutoConfiguration {}
SpringBoot許多源碼裏面,都會出現條件註解,這就是Starter配置的核心之一。
spring.factories
文件,這個是springboot啓動會掃描的文件,然後尋找所依賴的jar包,寫法如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration =
//等於號後面是定義的starter的路徑
com.example.autocinfigure.StarterAutoConfigure
這裏舉個栗子:我們自己開發項目引入的mybatis-starter如下
同樣能看到mybatis的spring.factories文件
打開後,如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
開發自己的Starter
所謂starter,就是一個普通的Maven項目
我們要實現的starter很簡單, 提供一個Service, 包含一個sayHello()
方法
主要分爲下面幾步驟
- 創建Maven項目
- 編寫service
- 編寫屬性類
- 編寫自動配置類
- 創建spring.factories文件,打包
創建Maven工程
新建一個普通Maven項目
-
命名:這裏說下artifactId的命名問題,Spring 官方 Starter通常命名爲
spring-boot-starter-{name}
如spring-boot-starter-web
。Spring官方建議非官方Starter命名應遵循
{name}-spring-boot-starter
的格式。 -
注意其中
spring-boot-configuration-processor
的作用是編譯時生成spring-configuration-metadata.json
, 此文件主要給IDE使用,用於提示使用。如在intellij idea中,當配置此jar相關配置屬性在application.yml
, 你可以用ctlr+鼠標左鍵,IDE會跳轉到你配置此屬性的類中。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xncoding</groupId>
<artifactId>simple-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>simple-spring-boot-starter</name>
<description>一個簡單的自定義starter</description>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependencies>
<!-- @ConfigurationProperties annotation processing (metadata for IDEs)
生成spring-configuration-metadata.json類,需要引入此類-->
<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>
</project>
編寫Servcie
public class ExampleService {
private String prefix;
private String suffix;
public ExampleService(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
public String wrap(String word) {
return prefix + word + suffix;
}
}
編寫配置屬性類
@ConfigurationProperties("example.service")
public class ExampleServiceProperties {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
其中@ConfigurationPropeties
註解是讀取 application.yml中 以 example.service 開頭的屬性
編寫自動配置類
@Configuration
@ConditionalOnClass(ExampleService.class)
@EnableConfigurationProperties(ExampleServiceProperties.class)
public class ExampleAutoConfigure {
private final ExampleServiceProperties properties;
@Autowired
public ExampleAutoConfigure(ExampleServiceProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example.service", value = "enabled",havingValue = "true")
ExampleService exampleService (){
return new ExampleService(properties.getPrefix(),properties.getSuffix());
}
}
解釋下用到的幾個和Starter相關的註解:
1. @ConditionalOnClass,當classpath下發現該類的情況下進行自動配置。
2. @ConditionalOnMissingBean,當Spring Context中不存在該Bean時。
3. @ConditionalOnProperty(prefix = "example.service",value = "enabled",havingValue = "true"),當配置文件中example.service.enabled=true時。
最後一步:添加spring.factories文件
最後一步,在resources/META-INF/
下創建spring.factories
文件,內容供參考下面:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xncoding.starter.config.ExampleAutoConfigure
如果有多個自動配置類,用逗號分隔換行即可。
OK,完事,運行 mvn:install
打包安裝,一個Spring Boot Starter便開發完成了。
測試
打包好了當然要測試一下看看了。另外創建一個SpringBoot工程,在maven中引入這個starter依賴, 然後在單元測試中引入這個Service看看效果。
<dependency>
<groupId>com.xncoding</groupId>
<artifactId>simple-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
修改application.yml
配置文件,添加如下內容:
example.service:
enabled: true
prefix: ppp
suffix: sss
測試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Autowired
private ExampleService exampleService;
@Test
public void testStarter() {
System.out.println(exampleService.wrap("hello"));
}
}
運行結果
ppphellosss