我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中错忱。Starter 為我們帶來了眾多的自動(dòng)化配置,有了這些自動(dòng)化配置,我們可以不費(fèi)吹灰之力就能搭建一個(gè)生產(chǎn)級(jí)開發(fā)環(huán)境楼咳,有的小伙伴會(huì)覺得這個(gè) Starter 好神奇呀!
其實(shí) Starter 也都是 Spring + SpringMVC 中的基礎(chǔ)知識(shí)點(diǎn)實(shí)現(xiàn)的,今天我們就 自己來寫一個(gè) Starter 烛恤,慢慢揭開 Starter 的神秘面紗!
自定義Starter
其實(shí) Starter 的核心就是條件注解 @Conditional 母怜,當(dāng) classpath 下存在某一個(gè) Class 時(shí),某個(gè)配置才會(huì)生效缚柏。
所謂的 Starter 苹熏,其實(shí)就是一個(gè)普通的 Maven 項(xiàng)目,因此我們自定義 Starter 币喧,需要首先創(chuàng)建一個(gè)普通 的 Maven 項(xiàng)目轨域,創(chuàng)建完成后,添加 Starter 的自動(dòng)化配置類即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
配置完成后杀餐,我們首先創(chuàng)建一個(gè) HelloProperties 類干发,用來接受 application.properties 中注入的值, 如下:
@ConfigurationProperties(prefix = "tp")
public class HelloProperties {
private static final String DEFAULT_NAME = "TP";
private static final String DEFAULT_MSG = "漸行漸遠(yuǎn)";
private String name = DEFAULT_NAME;
private String msg = DEFAULT_MSG;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
這個(gè)配置類很好理解史翘,將 application.yml 中配置的屬性值直接注入到這個(gè)實(shí)例中枉长, @ConfigurationProperties 類型安全的屬性注入冀续,即將 application.yml 文件中前綴為
tp 的屬性注入到這個(gè)類對應(yīng)的屬性上, 最后使用時(shí)候搀暑,application.yml 中的配置文件沥阳, 大概如下:
tp:
name: zhangsan
msg: boot
配置完成 HelloProperties 后,接下來我們來定義一個(gè) HelloService 自点,然后定義一個(gè)簡單的 say 方法桐罕, HelloService 的定義如下:
public class HelloService {
private String msg;
private String name;
public String sayHello() {
return name + " say: " + msg + " !";
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
這個(gè)很簡單,沒啥好說的桂敛。
接下來就是我們的重軸戲功炮,自動(dòng)配置類的定義,用了很多別人定義的自定義類之后术唬,我們也來自己定義一個(gè)類薪伏。代碼如下:
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
@ConditionalOnClass(HelloService.class)
public class HelloServiceAutoConfiguration {
@Autowired
private HelloProperties helloProperties;
@Bean
HelloService helloService(){
HelloService helloService = new HelloService();
helloService.setName(helloProperties.getName());
helloService.setMsg(helloProperties.getMsg());
return helloService;
}
}
關(guān)于這一段自動(dòng)配置,解釋如下:
- 首先 @Configuration 注解表明這是一個(gè)配置類粗仓。
- @EnableConfigurationProperties 注解是使我們之前配置的 @ConfigurationProperties 生效嫁怀, 讓配置的屬性成功的進(jìn)入 Bean 中。
- @ConditionalOnClass 表示當(dāng)項(xiàng)目當(dāng)前 classpath 下存在 HelloService 時(shí)借浊,后面的配置才生效塘淑。 自動(dòng)配置類中首先注入 HelloProperties ,這個(gè)實(shí)例中含有我們在 application.yml 中配置的相關(guān)數(shù)據(jù)蚂斤。
- 提供一個(gè) HelloService 的實(shí)例存捺,將 HelloProperties 中的值注入進(jìn)去。
做完這一步之后曙蒸,我們的自動(dòng)化配置類就算是完成了捌治,接下來還需要一個(gè) spring.factories 文件,那么這個(gè)文件是干嘛的呢? 大家知道我們的 Spring Boot 項(xiàng)目的啟動(dòng)類都有一個(gè) @SpringBootApplication 注解纽窟,這個(gè)注解的定義如下:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
大家看到這是一個(gè)組合注解肖油,其中的一個(gè)組合項(xiàng)就是 @EnableAutoConfiguration ,這個(gè)注解是干嘛的呢?
@EnableAutoConfiguration 表示啟用 Spring 應(yīng)用程序上下文的自動(dòng)配置臂港,該注解會(huì)自動(dòng)導(dǎo)入一個(gè)名為 AutoConfigurationImportSelector 的類,而這個(gè)類會(huì)去讀取一個(gè)名為 spring.factories 的文件, spring.factories 中則定義需要加載的自動(dòng)化配置類森枪,我們打開任意一個(gè)框架的 Starter ,都能看到它有 一個(gè) spring.factories 文件趋艘,例如 druid 的 Starter 如下:
那么我們自定義 Starter 當(dāng)然也需要這樣一個(gè)文件疲恢,我們首先在 Maven 項(xiàng)目的 resources 目錄下創(chuàng)建 一個(gè)名為 META-INF 的文件夾,然后在文件夾中創(chuàng)建一個(gè)名為 spring.factories 的文件瓷胧,文件內(nèi)容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tp.spring.boot.starter.config.HelloServiceAutoConfiguration
在這里指定我們的自動(dòng)化配置類的路徑即可显拳,如此之后我們的自動(dòng)化配置類就算完成了。
本地安裝
在正常的公司開發(fā)中搓萧,我們可能需要將這個(gè)自定義starter發(fā)不到公司的maven私服杂数,這里我們就簡單起見宛畦,將這個(gè)starter直接安裝到本地然后進(jìn)行測試:
- 我們在前文中的boot-demo項(xiàng)目中引入我們自定義的stater:
<!-- 自定義starter -->
<dependency>
<groupId>com.tp</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0-snapshot</version>
</dependency>
- 編寫測試類,測試:
@SpringBootTest
class BootDemoApplicationTests {
@Autowired
private HelloService helloService;
@Test
void starterTest(){
System.out.println(helloService.sayHello());
}
}
效果如下:
至此我們自定義一個(gè)starter就完成了