圖文并茂,Spring Boot Starter 萬字詳解!還有誰不會?
一、SpringBoot的starter簡介
1.1 什么是starter(場景啟動器)
在SpringBoot出現(xiàn)之前,如果我們想使用SpringMVC來構(gòu)建我們的web項(xiàng)目,必須要做的幾件事情如下:
首先項(xiàng)目中需要引入SpringMVC的依賴 在web.xml中注冊SpringMVC的 DispatcherServlet,并配置url映射編寫 springmcv-servlet.xml,在其中配置SpringMVC中幾個(gè)重要的組件,處理映射器(HandlerMapping)、處理適配器(HandlerAdapter)、視圖解析器(ViewResolver)在 applicationcontext.xml文件中引入springmvc-servlet.xml文件…
以上這幾步只是配置好了SpringMVC,如果我們還需要與數(shù)據(jù)庫進(jìn)行交互,就要在application.xml中配置數(shù)據(jù)庫連接池DataSource,如果需要數(shù)據(jù)庫事務(wù),還需要配置TransactionManager…
這就是使用Spring框架開發(fā)項(xiàng)目帶來的一些的問題:
依賴導(dǎo)入問題: 每個(gè)項(xiàng)目都需要來單獨(dú)維護(hù)自己所依賴的jar包,在項(xiàng)目中使用到什么功能就需要引入什么樣的依賴。手動導(dǎo)入依賴容易出錯(cuò),且無法統(tǒng)一集中管理 配置繁瑣: 在引入依賴之后需要做繁雜的配置,并且這些配置是每個(gè)項(xiàng)目來說都是必要的,例如web.xml配置(Listener配置、Filter配置、Servlet配置)、log4j配置、數(shù)據(jù)庫連接池配置等等。這些配置重復(fù)且繁雜,在不同的項(xiàng)目中需要進(jìn)行多次重復(fù)開發(fā),這在很大程度上降低了我們的開發(fā)效率
而在SpringBoot出現(xiàn)之后,它為我們提供了一個(gè)強(qiáng)大的功能來解決上述的兩個(gè)痛點(diǎn),這就是SpringBoot的starters(場景啟動器)。
Spring Boot通過將我們常用的功能場景抽取出來,做成的一系列場景啟動器,這些啟動器幫我們導(dǎo)入了實(shí)現(xiàn)各個(gè)功能所需要依賴的全部組件,我們只需要在項(xiàng)目中引入這些starters,相關(guān)場景的所有依賴就會全部被導(dǎo)入進(jìn)來,并且我們可以拋棄繁雜的配置,僅需要通過配置文件來進(jìn)行少量的配置就可以使用相應(yīng)的功能。
二、SpringBoot場景啟動器的原理
在導(dǎo)入的starter之后,SpringBoot主要幫我們完成了兩件事情:
相關(guān)組件的自動導(dǎo)入 相關(guān)組件的自動配置
這兩件事情統(tǒng)一稱為SpringBoot的自動配置
2.1 自動配置原理
2.1.1 自動配置類的獲取與注入
我們從主程序入口來探索一下整個(gè)過程的原理:
@SpringBootApplication?//標(biāo)注這個(gè)類是一個(gè)springboot的應(yīng)用
public?class?CommunityApplication?{
????public?static?void?main(String[]?args)?{
????????//將springboot應(yīng)用啟動
????????SpringApplication.run(CommunityApplication.class,?args);
????}
}
@SpringBootApplication注解內(nèi)部結(jié)構(gòu)如下圖所示:

AutoConfigurationImportSelector :重點(diǎn)看該類中重寫的selectImports方法,看下它返回的字符串?dāng)?shù)組是如何得來的:

我們可以去到上邊提到的spring.factories文件中去看一下,找到spring官方提供的spring-boot-autoconfigure包,在其下去找一下該文件:

可以看到這個(gè)就是SpringBoot官方為我們提供的所有自動配置類的候選列表。我們可以在其中找到一個(gè)我們比較熟悉的自動配置類去看一下它內(nèi)部的實(shí)現(xiàn):
可以看到這些一個(gè)個(gè)的都是JavaConfig配置類,而且都通過@Bean注解向容器中注入了一些Bean

結(jié)論:
SpringBoot在啟動的時(shí)候從類路徑下的 META-INF/spring.factories中獲取EnableAutoConfiguration指定的所有自動配置類的全限定類名將這些自動配置類導(dǎo)入容器,自動配置類就生效,幫我們進(jìn)行自動配置工作; 整個(gè)J2EE的整體解決方案和自動配置都在 spring-boot-autoconfigure的jar包中;它會給容器中導(dǎo)入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中導(dǎo)入這個(gè)場景需要的所有組件,并配置好這些組件 ; 有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作;
2.1.2 自動配置的過程
自動配置類被注入到容器當(dāng)中后,會幫我們進(jìn)行組件的自動配置和自動注入的工作,我們以HttpEncodingAutoConfiguration(Http編碼自動配置)為例解釋這個(gè)過程:
首先我們先看下SpringBoot中配置文件與POJO類之間映射的方法,這是進(jìn)行自動配置的基礎(chǔ)。?
配置集中化管理:SpringBoot中所有可配置項(xiàng)都集中在一個(gè)文件中(application.yml),這個(gè)文件中的配置通過@ConfigurationProperties注解來與我們程序內(nèi)部定義的POJO類來產(chǎn)生關(guān)聯(lián),這些POJO類統(tǒng)一命名為xxxProperties,并且這些xxxProperties類中各個(gè)屬性字段都有自己的默認(rèn)值,這也是SpringBoot約定大于配置理念的體現(xiàn),盡可能減少用戶做選擇的次數(shù),但同時(shí)又不失靈活性。只要我們想,配置文件中的配置隨時(shí)可以覆蓋默認(rèn)值。

之后,通過配合@EnableConfigurationProperties注解,就可以自動將與配置文件綁定好的這個(gè)類注入到容器中供我們使用。
自動配置類的工作流程:
根據(jù)限定的條件向容器中注入組件 使用xxxProperties對注入的組件的相關(guān)屬性進(jìn)行配置
//表示這是一個(gè)配置類,和以前編寫的配置文件一樣,也可以給容器中添加組件;
@Configuration
//將與配置文件綁定好的某個(gè)類注入到容器中,使其生效
//進(jìn)入這個(gè)HttpProperties查看,將配置文件中對應(yīng)的值和HttpProperties綁定起來;
//并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties(HttpProperties.class)
//Spring底層@Conditional注解
//根據(jù)不同的條件判斷,如果滿足指定的條件,整個(gè)配置類里面的配置就會生效;
//這里的意思就是判斷當(dāng)前應(yīng)用是否是web應(yīng)用,如果是,當(dāng)前配置類生效
@ConditionalOnWebApplication(type?=?ConditionalOnWebApplication.Type.SERVLET)
//判斷系統(tǒng)中有沒有CharacterEncodingFilter這個(gè)類,如果有配置類才生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//判斷配置文件中是否存在某個(gè)配置:spring.http.encoding.enabled;
//matchIfMissing?=?true表明即使我們配置文件中不配置pring.http.encoding.enabled=true,該配置類也是默認(rèn)生效的;
@ConditionalOnProperty(prefix?=?"spring.http.encoding",?value?=?"enabled",?matchIfMissing?=?true)
public?class?HttpEncodingAutoConfiguration?{
??//該類已經(jīng)與配置文件綁定了
?private?final?HttpProperties.Encoding?properties;
??//構(gòu)建該自動配置類時(shí)將與配置文件綁定的配置類作為入?yún)鬟f進(jìn)去
?public?HttpEncodingAutoConfiguration(HttpProperties?properties)?{
??this.properties?=?properties.getEncoding();
?}
?@Bean
?@ConditionalOnMissingBean
?public?CharacterEncodingFilter?characterEncodingFilter()?{
??CharacterEncodingFilter?filter?=?new?OrderedCharacterEncodingFilter();
??filter.setEncoding(this.properties.getCharset().name());?//注入bean時(shí)使用配置類中屬性的值進(jìn)行初始化,相當(dāng)于將配置文件中的值映射到了組件的某些屬性上
??filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
??filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
??return?filter;?//注入配置好的bean
?}
}
一句話總結(jié)下自動配置類的工作過程 :
首先容器會根據(jù)當(dāng)前不同的條件判斷,決定這個(gè)配置類是否生效! 一但這個(gè)配置類生效;這個(gè)配置類就會給容器中添加相應(yīng)組件; 這些組件的屬性是從對應(yīng)的properties類中獲取的,這些類里面的每一個(gè)屬性又是和配置文件綁定的; 所有在配置文件中能配置的屬性都是在xxxxProperties類中封裝著,配置文件可以配置什么內(nèi)容,可以參照該前綴對應(yīng)的屬性類中的屬性字段
//從配置文件中獲取指定的值和bean的屬性進(jìn)行綁定
@ConfigurationProperties(prefix?=?"spring.http")?
public?class?HttpProperties?{
????//?.....
}
2.2 SpringBoot自動配置使用總結(jié)
SpringBoot啟動會加載大量的自動配置類 我們首先可以看我們需要的功能有沒有在SpringBoot默認(rèn)寫好的自動配置類當(dāng)中; 我們再來看這個(gè)自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了) 給容器中自動配置類添加組件的時(shí)候,會從properties類中獲取某些屬性。我們只需要在配置文件中指定這些屬性的值即可; xxxxAutoConfigurartion:自動配置類;給容器中添加組件xxxxProperties:封裝配置文件中相關(guān)屬性;
了解完自動裝配的原理后,我們來關(guān)注一個(gè)細(xì)節(jié)問題,自動配置類必須在一定的條件下才能生效;@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必須是@Conditional指定的條件成立,才給容器中添加組件,配置里面的所有內(nèi)容才生效;

那么多的自動配置類,必須在一定的條件下才能生效;也就是說,我們加載了這么多的配置類,但不是所有的都生效了。
我們怎么知道哪些自動配置類生效?
我們可以通過啟用 debug=true屬性;來讓控制臺打印自動配置報(bào)告,這樣我們就可以很方便的知道哪些自動配置類生效;
#在配置文件中開啟springboot的調(diào)試類
debug=true
Positive matches:(自動配置類啟用的:正匹配)
Positive?matches:
-----------------
???AopAutoConfiguration?matched:
??????-?@ConditionalOnClass?found?required?classes?'org.springframework.context.annotation.EnableAspectJAutoProxy',?'org.aspectj.lang.annotation.Aspect',?'org.aspectj.lang.reflect.Advice',?'org.aspectj.weaver.AnnotatedElement'?(OnClassCondition)
??????-?@ConditionalOnProperty?(spring.aop.auto=true)?matched?(OnPropertyCondition)
???AopAutoConfiguration.CglibAutoProxyConfiguration?matched:
??????-?@ConditionalOnProperty?(spring.aop.proxy-target-class=true)?matched?(OnPropertyCondition)
???AuditAutoConfiguration#auditListener?matched:
??????-?@ConditionalOnMissingBean?(types:?org.springframework.boot.actuate.audit.listener.AbstractAuditListener;?SearchStrategy:?all)?did?not?find?any?beans?(OnBeanCondition)
???AuditAutoConfiguration#authenticationAuditListener?matched:
??????-?@ConditionalOnClass?found?required?class?'org.springframework.security.authentication.event.AbstractAuthenticationEvent'?(OnClassCondition)
??????-?@ConditionalOnMissingBean?(types:?org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener;?SearchStrategy:?all)?did?not?find?any?beans?(OnBeanCondition)
Negative matches:(沒有啟動,沒有匹配成功的自動配置類:負(fù)匹配)
Negative?matches:
-----------------
???ActiveMQAutoConfiguration:
??????Did?not?match:
?????????-?@ConditionalOnClass?did?not?find?required?class?'javax.jms.ConnectionFactory'?(OnClassCondition)
???AopAutoConfiguration.JdkDynamicAutoProxyConfiguration:
??????Did?not?match:
?????????-?@ConditionalOnProperty?(spring.aop.proxy-target-class=false)?did?not?find?property?'proxy-target-class'?(OnPropertyCondition)
???AppOpticsMetricsExportAutoConfiguration:
??????Did?not?match:
?????????-?@ConditionalOnClass?did?not?find?required?class?'io.micrometer.appoptics.AppOpticsMeterRegistry'?(OnClassCondition)
???ArtemisAutoConfiguration:
??????Did?not?match:
?????????-?@ConditionalOnClass?did?not?find?required?class?'javax.jms.ConnectionFactory'?(OnClassCondition)
Exclusions、Unconditional classes(排除的、沒有限定條件的自動配置類):
Exclusions:
-----------
????org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Unconditional?classes:
----------------------
????org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
????org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration
????org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration
????org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration
三、自定義場景啟動器
現(xiàn)在我們已經(jīng)了解了場景啟動器的概念以及其隱藏在背后的自動配置的原理,我們就可以自己來對SpringBoot進(jìn)行功能拓展,定義我們自己的場景啟動器。另外,歡迎關(guān)注公眾號江南一點(diǎn)雨,后臺回復(fù)“面試題”,送你一份面試題寶典!
3.1 starter的命名規(guī)范
官方命名空間
前綴:spring-boot-starter- 模式:spring-boot-starter-模塊名 舉例:spring-boot-starter-web、spring-boot-starter-jdbc
自定義命名空間
后綴:-spring-boot-starter 模式:模塊-spring-boot-starter 舉例:mybatis-spring-boot-starter
3.2 starter模塊整體結(jié)構(gòu)
通過上邊的介紹,可以總結(jié)starter的整體實(shí)現(xiàn)邏輯主要由兩個(gè)基本部分組成:
xxxAutoConfiguration:自動配置類,對某個(gè)場景下需要使用到的一些組件進(jìn)行自動注入,并利用xxxProperties類來進(jìn)行組件相關(guān)配置
xxxProperties:某個(gè)場景下所有可配置屬性的集成,在配置文件中配置可以進(jìn)行屬性值的覆蓋
按照SpringBoot官方的定義,Starer的作用就是依賴聚合,因此直接在starter內(nèi)部去進(jìn)行代碼實(shí)現(xiàn)是不符合規(guī)定的,starter應(yīng)該只起到依賴導(dǎo)入的作用,而具體的代碼實(shí)現(xiàn)應(yīng)該去交給其他模塊來實(shí)現(xiàn),然后在starter中去引用該模塊即可,因此整體的starter的構(gòu)成應(yīng)該如下圖所示:

可見starter模塊依賴了兩部分,一部分是一些常用依賴,另一部分就是對自動配置模塊的依賴,而xxxAutoConfiguration與xxxProperties的具體實(shí)現(xiàn),都封裝在自動配置模塊中,starter實(shí)際是通過該模塊來對外提供相應(yīng)的功能。
3.3 autoconfigure模塊開發(fā)
3.3.1 依賴引入
首先所有的自動配置模塊都要引入兩個(gè)jar包依賴:
<dependencies>
?????<dependency>
?????????<groupId>org.springframework.bootgroupId>
?????????<artifactId>spring-boot-autoconfigureartifactId>?
????????
?????dependency>
?????<dependency>
?????????<groupId>org.springframework.bootgroupId>
?????????<artifactId>spring-boot-configuration-processorartifactId>?
???????
?????????<optional>trueoptional>
?????dependency>
?<dependencies>
其他依賴的選擇根據(jù)項(xiàng)目需要進(jìn)行添加即可
3.3.2 xxxAutoConfiguration的實(shí)現(xiàn)
autoconfigure模塊中最重要的就是自動配置類的編寫,它為我們實(shí)現(xiàn)組件的自動配置與自動注入。
在編寫自動配置類的時(shí)候,我們應(yīng)該要考慮向容器中注入什么組件,如何去配置它。
@Configuration?
@ConditionalOnxxx
@ConditionalOnxxx//限定自動配置類生效的一些條件
@EnableConfigurationProperties(xxxProperties.class)
public?class?xxxAutoConfiguration?{
?@Autowired
?private?xxxProperties?properties;
????@Bean
????public?static?BeanYouNeed?beanYouNeed()?{
?????BeanYouNeed?bean?=?new?BeanYouNeed()
?????bean.setField(properties.get(field));
?????bean.setField(properties.get(field));
?????bean.setField(properties.get(field));
????????......
????}
}
3.3.3 xxxProperties的實(shí)現(xiàn)
這是跟配置文件相綁定的類,里邊的屬性就是我們可以在配置文件中配置的內(nèi)容,然后通過@ConfigurationProperties將其與配置文件綁定:
@ConfigurationProperties(prefix?=?"your?properties")?//使用@ConfigurationProperties注解綁定配置文件
public?class?xxxProperties?{
????private?boolean?enabled?=?true;
????private?String?clientId;
????private?String?beanName;
????private?String?scanBasePackage;
????private?String?path;
????private?String?token;
}
3.3.4 配置spring.factories文件
在resource目錄下新建META-INF文件夾,在文件夾下新建spring.factories文件,并添加寫好的xxxAutoConfiguration類:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.meituan.xframe.boot.mcc.autoconfigure.xxxAutoConfiguration
3.4 Starter模塊開發(fā)
starter模塊中只進(jìn)行依賴導(dǎo)入,在pom文件中添加對autoconfigure模塊的依賴,并添加一些其他必要的依賴項(xiàng):
<dependencies>
?================================================================
?
?????<dependency>
?????????<groupId>com.test.startergroupId>
?????????<artifactId>xxx-spring-boot-autoconfigureartifactId>
?????dependency>
?===============================================================
?
?????<dependency>
?????????<groupId>commons-collectionsgroupId>
?????????<artifactId>commons-collectionsartifactId>
?????dependency>
?dependencies>
這兩個(gè)模塊都開發(fā)完成之后,通過mvn install命令或者deploy命令將包發(fā)布到本地或者中央倉庫,即可直接在其他項(xiàng)目中引用我們自定義的starter模塊了
