詳解SpringBoot如何自定義Starter
閱讀收獲
學(xué)會(huì)自定義Spring-Boot-Starter
理解SpringBoot自動(dòng)配置原理
本章源碼下載
什么是Starter
Starter是Spring Boot中的一個(gè)非常重要的概念,Starter相當(dāng)于模塊,它能將模塊所需的依賴(lài)整合起來(lái)并對(duì)模塊內(nèi)的Bean根據(jù)環(huán)境( 條件)進(jìn)行自動(dòng)配置。
使用者只需要依賴(lài)相應(yīng)功能的Starter,無(wú)需做過(guò)多的配置和依賴(lài),Spring Boot就能自動(dòng)掃描并加載相應(yīng)的模塊并設(shè)置默認(rèn)值,做到開(kāi)箱即用
為什么使用Starter
在我們的日常開(kāi)發(fā)工作中,經(jīng)常會(huì)有一些獨(dú)立于業(yè)務(wù)之外的配置模塊,我們經(jīng)常將其放到一個(gè)特定的包下,然后如果另一個(gè)工程需要復(fù)用這塊功能的時(shí)候,需要將代碼硬拷貝到另一個(gè)工程,重新集成一遍,麻煩至極。
如果我們將這些可獨(dú)立于業(yè)務(wù)代碼之外的功配置模塊封裝成一個(gè)個(gè)starter,并在starter中設(shè)置好默認(rèn)值,復(fù)用的時(shí)候只需要將其在pom中引用依賴(lài)即可,Spring Boot為我們完成自動(dòng)裝配,做到開(kāi)箱即用。
Springboot自動(dòng)配置
SpringBoot中的starter是一種非常重要的機(jī)制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進(jìn)starter,應(yīng)用者只需要在maven中引入starter依賴(lài),Spring Boot就能自動(dòng)掃描各個(gè)jar包下classpath路徑的spring.factories文件,加載自動(dòng)配置類(lèi)信息,加載相應(yīng)的bean信息并啟動(dòng)相應(yīng)的默認(rèn)配置。
Spring Boot提供了針對(duì)日常企業(yè)應(yīng)用研發(fā)各種場(chǎng)景的spring-boot-starter依賴(lài)模塊。所有這些依賴(lài)模塊都遵循著約定成俗的默認(rèn)配置,并允許我們調(diào)整這些配置,即遵循“約定大于配置”的理念。
大家可以看看我之前寫(xiě)的一篇文章,詳細(xì)介紹了springboot自動(dòng)配置的流程:一文搞懂SpringBoot自動(dòng)配置原理
spring.factories
Spring Boot會(huì)默認(rèn)掃描跟啟動(dòng)類(lèi)平級(jí)的包,如果我們的Starter跟啟動(dòng)類(lèi)不在同一個(gè)主包下,需要通過(guò)配置spring.factories文件來(lái)配置生效,SpringBoot默認(rèn)加載各個(gè)jar包下classpath路徑的spring.factories文件,配置的key為org.springframework.boot.autoconfigure.EnableAutoConfiguration
Starter開(kāi)發(fā)常用注解
注解使用已經(jīng)大大方便我們開(kāi)發(fā),再也不需要寫(xiě)xml配置文件了,SpringBoot經(jīng)過(guò)查找spring.factories文件,加載自動(dòng)配置類(lèi),而自動(dòng)配置類(lèi)中定義了各種運(yùn)行時(shí)判斷條件,如@ConditionalOnMissingBean(A.class)等,只要ioc容器中沒(méi)有指定的A類(lèi)型的bean信息,該配置文件才會(huì)生效。
@Conditional是Spring4新提供的注解,它的作用是按照一定的條件進(jìn)行判斷,滿(mǎn)足條件給容器注冊(cè)bean。
1.屬性映射注解
- @ConfigurationProperties :配置文件屬性值和實(shí)體類(lèi)的映射
- @EnableConfigurationProperties:和@ConfigurationProperties配合使用,把@ConfigurationProperties修飾的類(lèi)加入ioc容器。
2.配置bean注解
- @Configuration :標(biāo)識(shí)該類(lèi)為配置類(lèi),并把該類(lèi)注入ioc容器
- @Bean :一般在方法上使用,聲明一個(gè)bean,bean名稱(chēng)默認(rèn)是方法名稱(chēng),類(lèi)型為返回值。
3.條件注解
@Conditional:是根據(jù)條件類(lèi)創(chuàng)建特定的Bean,條件類(lèi)需要實(shí)現(xiàn)Condition接口,并重寫(xiě)matches接口來(lái)構(gòu)造判斷條件。
@ConditionalOnBean :容器中存在指定bean,才會(huì)實(shí)例化一個(gè)Bean
@ConditionalOnMissingBean:容器中不存在指定bean,才會(huì)實(shí)例化一個(gè)Bean
@ConditionalOnClass:系統(tǒng)中有指定類(lèi),才會(huì)實(shí)例化一個(gè)Bean
@ConditionalOnMissingClass:系統(tǒng)中沒(méi)有指定類(lèi),才會(huì)實(shí)例化一個(gè)Bean
@ConditionalOnExpression:當(dāng)SpEl表達(dá)式為true的時(shí)候,才會(huì)實(shí)例化一個(gè)Bean
@AutoConfigureAfter :在某個(gè)bean完成自動(dòng)配置后實(shí)例化這個(gè)bean
@AutoConfigureBefore :在某個(gè)bean完成自動(dòng)配置前實(shí)例化這個(gè)bean
@ConditionalOnJava :系統(tǒng)中版本是否符合要求
@ConditionalOnSingleCandidate:當(dāng)指定的Bean在容器中只有一個(gè),或者有多個(gè)但是指定了首選的Bean時(shí)觸發(fā)實(shí)例化
@ConditionalOnResource:類(lèi)路徑下是否存在指定資源文件
@ConditionalOnWebApplication:是web應(yīng)用
@ConditionalOnNotWebApplication:不是web應(yīng)用
@ConditionalOnJndi:JNDI指定存在項(xiàng)
@ConditionalOnProperty: 配置Configuration的加載規(guī)則
- prefix :配置屬性名稱(chēng)的前綴
- value :數(shù)組,獲取對(duì)應(yīng)property名稱(chēng)的值,與name不可同時(shí)使用
- name :數(shù)組,可與prefix組合使用,組成完整的配置屬性名稱(chēng),與value不可同時(shí)使用
- havingValue :比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置
- matchIfMissing :缺少該配置屬性時(shí)是否可以加載。如果為true,沒(méi)有該配置屬性時(shí)也會(huì)正常加載;反之則不會(huì)生效
Full全模式和Lite輕量級(jí)模式
@Configuration參數(shù)proxyBeanMethods:
Full 全模式(默認(rèn)):@Configuration(proxyBeanMethods = true)
同一配置類(lèi)下,當(dāng)直接調(diào)用@Bean修飾的方法注入的對(duì)象,則調(diào)用該方法會(huì)被代理,從ioc容器中取bean實(shí)列,所以實(shí)列是一樣的。即單實(shí)例對(duì)象,在該模式下SpringBoot每次啟動(dòng)都會(huì)判斷檢查容器中是否存在該組件
Lite 輕量級(jí)模式:@Configuration(proxyBeanMethods = false)
同一配置類(lèi)下,當(dāng)直接調(diào)用@Bean修飾的方法注入的對(duì)象,則調(diào)用該方法不會(huì)被代理,相當(dāng)于直接調(diào)用一個(gè)普通方法,會(huì)有構(gòu)造方法,但是沒(méi)有bean的生命周期,返回的是不同的實(shí)例。
注:proxyBeanMethods 是為了讓使用@Bean注解的方法被代理。而不是@Bean的單例多例的設(shè)置參數(shù)。
測(cè)試?yán)舆@里不展示,可以下載我的代碼查看
@Configuration(proxyBeanMethods = false)
public class AppConfig {
//放一份myBean到ioc容器
@Bean
public Mybean myBean() {
return new Mybean();
}
//放一份yourBean到ioc容器
@Bean
public YourBean yourBean() {
System.out.println("==========");
//注意:@Configuration(proxyBeanMethods = false):myBean()方法不代理,直接調(diào)用
//注意:@Configuration(proxyBeanMethods = true):myBean()方法代理,從ioc容器拿
return new YourBean(myBean());
}
}
什么時(shí)候用Full全模式,什么時(shí)候用Lite輕量級(jí)模式?
- 當(dāng)在你的同一個(gè)Configuration配置類(lèi)中,注入到容器中的bean實(shí)例之間有依賴(lài)關(guān)系時(shí),建議使用Full全模式
- 當(dāng)在你的同一個(gè)Configuration配置類(lèi)中,注入到容器中的bean實(shí)例之間沒(méi)有依賴(lài)關(guān)系時(shí),建議使用Lite輕量級(jí)模式,以提高springboot的啟動(dòng)速度和性能
Starter命名規(guī)范
Spring官方Starter通常命名為spring-boot-starter-{name}如:spring-boot-starter-web
Spring官方建議非官方Starter命名應(yīng)遵循{name}-spring-boot-starter的格式:如mybatis-spring-boot-starter。
開(kāi)發(fā)Starter
1. 創(chuàng)建Starter項(xiàng)目

新建項(xiàng)目后,要?jiǎng)h除main啟動(dòng)類(lèi)
2. 添加依賴(lài)
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.ljw</groupId>
<artifactId>ljw-spring-boot-starter</artifactId>
<version>1.0</version>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 包含自動(dòng)配置的代碼-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 配置文件點(diǎn)擊可以跳轉(zhuǎn)實(shí)體-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
我們沒(méi)有main入口,需要去除pom文件中maven打包插件spring-boot-maven-plugin
spring-boot-configuration-processor作用:
- spring-boot-configuration-processor其實(shí)是一個(gè)注解處理器,在編譯階段干活的,一般在maven的聲明都是optional 為true
- 你在idea里面可以點(diǎn)擊port,進(jìn)到這個(gè)字段里面,還可以看到配置的提示信息
- 這是因?yàn)樵谀愕馁Y源文件里面有一個(gè)spring-configuration-metadata.json文件,這是spring配置的元數(shù)據(jù),是json形式
3. 編寫(xiě)屬性類(lèi)
@ConfigurationProperties可以定義一個(gè)配置信息類(lèi),和配置文件進(jìn)行映射
@ConfigurationProperties(prefix = "ljw.config")
public class HelloProperties {
private String name = "hello 默認(rèn)值!";
private int age = 8;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4. 自定義業(yè)務(wù)類(lèi)
這里可以模擬一些獲取了配置文件信息的進(jìn)行業(yè)務(wù)操作的業(yè)務(wù)類(lèi)
public class HelloService {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String hello() {
return "HelloService{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
5. 編寫(xiě)自動(dòng)配置類(lèi)
命名規(guī)范:XxxAutoConfiguration
@Configuration(proxyBeanMethods = false)
// 當(dāng)存在某個(gè)類(lèi)時(shí),此自動(dòng)配置類(lèi)才會(huì)生效
@ConditionalOnClass(value = {HelloService.class})
// 導(dǎo)入我們自定義的配置類(lèi),供當(dāng)前類(lèi)使用
@EnableConfigurationProperties(value = HelloProperties.class)
// 只有非web應(yīng)用程序時(shí)此自動(dòng)配置類(lèi)才會(huì)生效
@ConditionalOnWebApplication
//判斷l(xiāng)jw.config.flag的值是否為“true”, matchIfMissing = true:沒(méi)有該配置屬性時(shí)也會(huì)正常加載
@ConditionalOnProperty(prefix = "ljw.config", name = "flag", havingValue = "true", matchIfMissing = true)
public class HelloAutoConfiguration {
/**
* @param helloProperties 直接方法簽名入?yún)⒆⑷際elloProperties,也可以使用屬性注入
* @return
*/
@Bean
@ConditionalOnMissingBean(HelloService.class)
//@ConditionalOnProperty(prefix = "ljw.config", name = "flag", havingValue = "true", matchIfMissing = true)
public HelloService helloService(HelloProperties helloProperties) {
HelloService helloService = new HelloService();
//把獲取的信息注入
helloService.setName(helloProperties.getName());
helloService.setAge(helloProperties.getAge());
return helloService;
}
}
注:這里配置一個(gè)web應(yīng)用才能注入,并且ljw.config.flag的值是否為“true”或者不配置該key才能注入HelloService服務(wù)
6. 編寫(xiě)spring.factories
把自動(dòng)配置類(lèi)HelloAutoConfiguration配置到org.springframework.boot.autoconfigure.EnableAutoConfiguration的key下,springboot會(huì)自動(dòng)加載該文件并根據(jù)條件裝配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.ljw.starter.config.HelloAutoConfiguration
7. 編寫(xiě)配置提示文件(非必須)
additional-spring-configuration-metadata.json
配置additional-spring-configuration-metadata.json文件后,在開(kāi)發(fā)人員的IDE工具使用個(gè)人編寫(xiě)的配置讀取很有效的在application.properties或application.yml文件下完成提示。
我的配置:
{"properties": [
{
"name": "ljw.config.name",
"type": "java.lang.String",
"defaultValue": "hello 默認(rèn)值!這里配置的是提示,真正默認(rèn)值在Properties里面",
"description": "這是字符串名稱(chēng)啊."
},
{
"name": "ljw.config.age",
"defaultValue": 8,
"description": "這是int類(lèi)型的年齡啊.",
"deprecation": {
"reason": "過(guò)時(shí)原因.",
"replacement": "替代key是:ljw.config.age22",
"level": "warning"
}
}
]}
大家參考下面properties表格進(jìn)行配置上的理解。

deprecation每個(gè)properties元素的屬性中包含的JSON對(duì)象可以包含以下屬性:

spring-configuration-metadata.json
spring-configuration-metadata.json代碼量挺大的,為了方便我們可以通過(guò)IDE來(lái)生成,這里使用的是idea。
在idea設(shè)置中搜索Annotation Processors,接下來(lái)勾住Enable annonation processing就完成了。在編譯打包后的文件中看到自動(dòng)生成的spring-configuration-metadata.json。這個(gè)文件不用我們編寫(xiě)

下面是自動(dòng)生成的:
{
"groups": [
{
"name": "ljw.config",
"type": "com.ljw.starter.properties.HelloProperties",
"sourceType": "com.ljw.starter.properties.HelloProperties"
}
],
"properties": [
{
"name": "ljw.config.name",
"type": "java.lang.String",
"description": "這是字符串名稱(chēng)啊.",
"sourceType": "com.ljw.starter.properties.HelloProperties",
"defaultValue": "hello 默認(rèn)值!這里配置的是提示,真正默認(rèn)值在Properties里面"
},
{
"name": "ljw.config.age",
"type": "java.lang.Integer",
"description": "這是int類(lèi)型的年齡啊.",
"sourceType": "com.ljw.starter.properties.HelloProperties",
"defaultValue": 8,
"deprecated": true,
"deprecation": {
"level": "warning",
"reason": "過(guò)時(shí)原因.",
"replacement": "替代key是:ljw.config.age22"
}
}
],
"hints": []
}
測(cè)試Starter
1. 前置環(huán)境
install打包自定義starter項(xiàng)目:ljw-spring-boot-starter
新建項(xiàng)目:ljw-test-spring-boot-starter
2. 添加依賴(lài)
引入打好包的自定義starter
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 測(cè)試web應(yīng)用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--自定義satrter-->
<dependency>
<groupId>com.ljw</groupId>
<artifactId>ljw-spring-boot-starter</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
3. 測(cè)試類(lèi)
@Service
public class TestController implements CommandLineRunner {
/**
* 注入自定義starter服務(wù)
*/
@Resource
private HelloService helloService;
@Override
public void run(String... args) throws Exception {
System.out.println(helloService.hello());
}
}
4. 修改配置文件
輸入前綴可以看出已經(jīng)有提示了

ljw.config.name=ljw hello! ljw.config.age=99 ljw.config.flag=true #不會(huì)注入 #ljw.config.flag=true1 # 可以看到哪些自動(dòng)配置了 debug=true
5. 運(yùn)行程序打印
HelloService{name='ljw hello!', age=99}
條件注入
- 如果沒(méi)有spring-boot-starter-web依賴(lài),不能注入服務(wù)HelloService
- 如果配置了ljw.config.flag,值不是true,不能注入服務(wù)HelloService;如果不配置ljw.config.flag,可以注入
6. 查看自動(dòng)配置類(lèi)生效的方法
通過(guò)啟用 debug=true 屬性,讓控制臺(tái)打印自動(dòng)配置報(bào)告,這樣就可以很方便地知道哪些自動(dòng)配置類(lèi)生效。
HelloAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.ljw.starter.service.HelloService' (OnClassCondition)
- @ConditionalOnWebApplication (required) found 'session' scope (OnWebApplicationCondition)
- @ConditionalOnProperty (ljw.config.flag=true) matched (OnPropertyCondition)
HelloAutoConfiguration#helloService matched:
- @ConditionalOnMissingBean (types: com.ljw.starter.service.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)
以上就是詳解SpringBoot如何自定義Starter的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot自定義Starter的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot Starter機(jī)制及整合tomcat的實(shí)現(xiàn)詳解
- springboot自定義starter方法及注解實(shí)例
- SpringBoot詳細(xì)分析自動(dòng)裝配原理并實(shí)現(xiàn)starter
- SpringBoot項(xiàng)目為何引入大量的starter?如何自定義starter?
- 教你利用SpringBoot寫(xiě)一個(gè)屬于自己的Starter
- SpringBoot如何自定義starter
- SpringBoot Starter依賴(lài)原理與實(shí)例詳解
- SpringBoot自定義啟動(dòng)器Starter流程詳解
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之加權(quán)無(wú)向圖的設(shè)計(jì)實(shí)現(xiàn)
加權(quán)無(wú)向圖是一種為每條邊關(guān)聯(lián)一個(gè)權(quán)重值或是成本的圖模型。這種圖能夠自然地表示許多應(yīng)用。這篇文章主要介紹了加權(quán)無(wú)向圖的設(shè)計(jì)與實(shí)現(xiàn),感興趣的可以了解一下2022-11-11
Java 8 Stream Api 中的 map和 flatMap 操作方法
Java 8提供了非常好用的 Stream API ,可以很方便的操作集合。今天通過(guò)這篇文章給大家分享Java 8 Stream Api 中的 map和 flatMap 操作方法,需要的朋友可以參考下2019-11-11
Netty分布式高性能工具類(lèi)FastThreadLocal和Recycler分析
這篇文章主要為大家介紹了Netty分布式高性能工具類(lèi)FastThreadLocal和Recycler分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
Java使用正則表達(dá)式驗(yàn)證手機(jī)號(hào)和電話號(hào)碼的方法
今天小編就為大家分享一篇關(guān)于Java使用正則表達(dá)式驗(yàn)證手機(jī)號(hào)和電話號(hào)碼的方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
Java二維數(shù)組查找功能代碼實(shí)現(xiàn)
這篇文章主要介紹了Java二維數(shù)組查找功能代碼實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Java編程用棧來(lái)求解漢諾塔問(wèn)題的代碼實(shí)例(非遞歸)
這篇文章主要介紹了Java編程用棧來(lái)求解漢諾塔問(wèn)題的代碼實(shí)例(非遞歸),具有一定參考價(jià)值,這里給大家分享下,供朋友們參考。2017-10-10
基于RabbitMQ的簡(jiǎn)單應(yīng)用(詳解)
下面小編就為大家分享一篇基于RabbitMQ的簡(jiǎn)單應(yīng)用(詳解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Nacos通過(guò)RefreshScope實(shí)現(xiàn)配置自動(dòng)更新的方式分享
這篇文章主要給大家介紹了Nacos如何通過(guò)RefreshScope實(shí)現(xiàn)配置自動(dòng)更新,文中給了兩種實(shí)現(xiàn)方式供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-09-09
如何解決hibernate一對(duì)多注解懶加載失效問(wèn)題
這篇文章主要介紹了解決hibernate一對(duì)多注解懶加載失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10

