SpringBoot原理之自動(dòng)配置機(jī)制詳解
前言
在當(dāng)下的java生態(tài)里,SpringBoot已經(jīng)成為事實(shí)上的開發(fā)標(biāo)準(zhǔn),絕大多數(shù)人現(xiàn)在都是面向SpringBoot編程。SpringBoot是對(duì)Spring的進(jìn)一步封裝,整合了分布式系統(tǒng)上下游所需的各種類庫和組件,并且實(shí)現(xiàn)了開箱即用,而這一切的底層基礎(chǔ)就是SpringBoot的自動(dòng)配置機(jī)制。
Spring配置類
Spring引入配置類是為了:1)替換冗長繁瑣的配置文件,2)提供更靈活的bean定義方式。使用@Configuration注解去標(biāo)記一個(gè)配置類,通過其中含有@Bean注解的方法去創(chuàng)建一個(gè)bean,如下代碼
@Configuration public class HelloAutoConfiguration { @Bean HelloService helloService() { return new HelloService; } }
即為一個(gè)簡單的配置類,并且定義了一個(gè)HelloService的bean。在此之上,Spring還提供了一套條件加載機(jī)制,可以去動(dòng)態(tài)控制一個(gè)配置類是否被加載。通過實(shí)現(xiàn)org.springframework.context.annotation.Condition接口,開發(fā)者就可以自己控制配置類的加載條件,滿足很多復(fù)雜的場景
SpringBoot自動(dòng)配置
介紹完了Spring的配置類,我們來看看SpringBoot是怎么利用這套機(jī)制去實(shí)現(xiàn)自動(dòng)配置的。
自動(dòng)配置的概念
首先,什么是自動(dòng)配置?我們看一下SpringBoot對(duì)于自動(dòng)配置類的定義:
Auto-configuration classes are regular Spring @Configuration beans. They are located using the SpringFactoriesLoader mechanism (keyed against this class). Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBeanannotations).
自動(dòng)配置類就是一個(gè)普通的@Configuration配置類,通常會(huì)帶有一些@Conditional條件注解,并且使用SpringFactoriesLoader機(jī)制去定位加載它們(并非都是如此,還有其他一些Spring固有的加載方式,比如通過@ComponentScan包掃描或者顯式@Import方式都可以讓它們被發(fā)現(xiàn))。
自動(dòng)配置的運(yùn)行機(jī)制
加載方式
自動(dòng)配置機(jī)制的啟用是通過@EnableAutoConfiguration注解去控制的,因此需要在SpringBoot工程的入口類上啟用該注解,但是通常,我們一般使用@SpringBootApplication來代替,后者是一個(gè)注解的合集,包含了一些必要的默認(rèn)配置,其中就有@EnableAutoConfiguration注解,其類的注釋上是這么描述的:
Indicates a Configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan.
它本身既標(biāo)識(shí)一個(gè)配置類,同時(shí)也開啟了自動(dòng)配置和組件掃描。
回到@EnableAutoConfiguration注解上,我們看一下該注解的定義
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
其中@Import(AutoConfigurationImportSelector.class)是功能生效的關(guān)鍵,該注解導(dǎo)入了AutoConfigurationImportSelector組件到Spring環(huán)境中,開啟自動(dòng)配置類的掃描加載工作,該類實(shí)現(xiàn)了接口org.springframework.context.annotation.ImportSelector
public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. * @return the class names, or an empty array if none */ String[] selectImports(AnnotationMetadata importingClassMetadata); ....其他省略 }
其中selectImports方法會(huì)在Spring啟動(dòng)時(shí)被調(diào)用,用于返回所有的自動(dòng)配置類,調(diào)用入口在org.springframework.context.annotation.ConfigurationClassParser類中,該類是Spring專門用來加載處理所有@Configuration配置類的,具體的加載細(xì)節(jié),限于篇幅問題,就不在本文中展開說明了,讀者們可自行去閱讀源碼,本人也許會(huì)在后續(xù)再另開一篇詳細(xì)說明。接著說selectImports方法,我們來看一下自動(dòng)配置類的加載過程,AutoConfigurationImportSelector對(duì)于該方法的具體實(shí)現(xiàn)為
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
isEnabled方法是一個(gè)開關(guān),用于控制是否啟用自動(dòng)配置,邏輯很簡單,略過不提,往下看,關(guān)鍵邏輯在getAutoConfigurationEntry方法中,跟下去
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
很容易看到加載邏輯在getCandidateConfigurations方法中,后續(xù)代碼是去重和過濾的過程,再往下看
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
這個(gè)方法就很簡單明顯了,直接調(diào)用SpringFactoriesLoader去加載對(duì)應(yīng)的內(nèi)容,接下來我們?cè)倭牧腟pringFactoriesLoader機(jī)制是怎么回事。
SpringFactoriesLoader機(jī)制
SpringFactoriesLoader直譯過來就是工廠加載機(jī)制,是Spring仿照J(rèn)ava的SPI機(jī)制實(shí)現(xiàn)的一套類加載機(jī)制,通過讀取模塊內(nèi)的META-INF/spring.factories文件來加載類,該文件為Properties格式,其中key部分是一個(gè)Class全限定名稱,可以是一個(gè)接口、抽象類或者注解等,而value部分是一個(gè)支持逗號(hào)分割的實(shí)現(xiàn)類列表,比如
而SpringFactoriesLoader就是Spring提供的一個(gè)用于讀取解析META-INF/spring.factories文件的工具類,通過傳入一個(gè)Class類型加載其對(duì)應(yīng)的實(shí)現(xiàn)類列表。
SpringFactoriesLoader如何應(yīng)用在自動(dòng)配置中
介紹完了SpringFactoriesLoader,我們來研究一下SpringBoot的自動(dòng)配置機(jī)制中是怎么使用它的,回到上面的getCandidateConfigurations方法中,我們看一下這一行
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
其中第一個(gè)參數(shù)是key對(duì)應(yīng)Class類型,第二個(gè)參數(shù)是用哪個(gè)ClassLoader去加載配置文件,我們看一下getSpringFactoriesLoaderFactoryClass這個(gè)方法返回的具體Class是什么
protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
很簡單,直接返回@EnableAutoConfiguration注解對(duì)應(yīng)的class類型,那么自動(dòng)配置類在META-INF/spring.factories文件中的配置方式就顯而易見了,上面截圖中最前面的部分
# AutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\ org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
就是對(duì)應(yīng)的自動(dòng)配置類了。這些被配置在此處的類都會(huì)被作為自動(dòng)配置類加載到Spring中,然后進(jìn)行相應(yīng)的處理,發(fā)揮出每個(gè)類的功能作用。
小結(jié)
SpringBoot的自動(dòng)配置機(jī)制就簡單介紹到這里了,相信看官們看完了之后也都有了一些了解,當(dāng)然這篇文章里還有很多相關(guān)內(nèi)容沒有涉及到,包括自動(dòng)配置類的條件加載方式、多個(gè)類之間的加載順序控制、排除和過濾機(jī)制,以及如何自定義自動(dòng)配置類、重寫框架默認(rèn)行為等等,這些內(nèi)容筆者會(huì)在后續(xù)的文章中再進(jìn)行詳細(xì)探討。
到此這篇關(guān)于SpringBoot原理之自動(dòng)配置機(jī)制的文章就介紹到這了,更多相關(guān)SpringBoot自動(dòng)配置機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?AOP?創(chuàng)建代理對(duì)象詳情
這篇文章介紹了Spring?AOP?創(chuàng)建代理對(duì)象詳情,主要介紹AOP?創(chuàng)建代理對(duì)象和上下文相關(guān)的內(nèi)容,下文分享具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05go語言題解LeetCode88合并兩個(gè)有序數(shù)組示例
這篇文章主要為大家介紹了go語言題解LeetCode88合并兩個(gè)有序數(shù)組示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java實(shí)現(xiàn)的圖片上傳工具類完整實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)的圖片上傳工具類,涉及java針對(duì)圖片文件的檢查、上傳、清除等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10將Springboot項(xiàng)目升級(jí)成Springcloud項(xiàng)目的圖文教程
本文主要介紹了將Springboot項(xiàng)目升級(jí)成Springcloud項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06Maven+SSM框架實(shí)現(xiàn)簡單的增刪改查
這篇文章主要介紹了Maven+SSM框架實(shí)現(xiàn)簡單的增刪改查,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03Java編程中隨機(jī)數(shù)的生成方式總結(jié)
在Java中利用自帶的類庫可以有三種途徑可以產(chǎn)生隨機(jī)數(shù),這里我們舉了一些簡單的例子來進(jìn)行Java編程中隨機(jī)數(shù)的生成方式總結(jié),需要的朋友可以參考下2016-05-05Java程序運(yùn)行之JDK,指令javac java解讀
這篇文章主要介紹了Java程序運(yùn)行之JDK,指令javac java,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01SpringBoot實(shí)現(xiàn)發(fā)送郵件任務(wù)
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)發(fā)送郵件任務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02Mac Book中Java環(huán)境變量設(shè)置的方法
本文給大家介紹mac book 中設(shè)置java環(huán)境變量的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-04-04