SpringBoot2入門自動配置原理及源碼分析
SpringBoot自動配置
之前為什么會去了解一些底層注解,其實就是為了后續(xù)更好的了解 springboot 底層的一些原理,比如自動配置原理。
一、@SpringBootApplication
從 MainApplication 中的@SpringBootApplication開始。

進入@SpringBootApplication,可以看到這是一個合成注解(紅框中是要關(guān)注的)。

1. @SpringBootConfiguration
這個注解干嘛的?
直接點進去,發(fā)現(xiàn)有一個@Configuration注解,那這不就是個配置類嘛。

進而也說明了,MainApplication 也是一個配置類。
2. @ComponentScan
這個已經(jīng)很熟悉了,可以指定掃描哪些 Spring 注解。
只不過這里,加了一些其他的過濾條件,暫時不關(guān)注。
3. @EnableAutoConfiguration
這個是最重要的注解了,聽名字就不一般,開啟自動配置。
點進去,發(fā)現(xiàn)也是一個合成注解(紅框需要關(guān)注)。

(1)@AutoConfigurationPackage
聽名字像是自動配置包?依舊點進去。

可以看到原來是導入了一個叫Registrar的組件,繼續(xù)點進 Registrar。

這里是利用Registrar()給容器中導入一系列組件,也就是批量注冊組件。
在這里打個斷點,debug 啟動一下。

registerBeanDefinitions()方法中有個傳參:
metadata,是注解的元信息,可以看到這個注解是被標注在com.pingguo.boot.MainApplication。
而在registerBeanDefinitions()方法體內(nèi),new 了一個AutoConfigurationPackages.PackageImports(),里面?zhèn)魅氲氖窃⒔猓ㄟ^getPackageNames()獲取到包名。
AutoConfigurationPackages.register(
registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])
);在 idea 中可以單獨執(zhí)行下片段代碼
(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames()。

選中右擊,再點擊 Evaluate。

得到的結(jié)果就是com.pingguo.boot。為什么是這個?因為注解標注在MainApplication類,而這個類就屬于com.pingguo.boot。
拿到包名之后,封裝到數(shù)組里,也就是上述代碼片段中的toArray(new String[0]),最后注冊進去。
所以,這里的Registrar()就是把指定的包下的所有組件批量注冊到容器中。
(2)@Import(AutoConfigurationImportSelector.class)
上面指定好默認包規(guī)則之后,就需要去導入需要的包了,利用的是AutoConfigurationImportSelector,繼續(xù)點進去看。

這里有個selectImports方法,這個方法決定了要具體導入哪些,返回的是一個數(shù)組。
方法體內(nèi),又是調(diào)用了getAutoConfigurationEntry()方法來獲取配置入口,進而再通過getConfigurations()方法獲取具體配置,最終轉(zhuǎn)成數(shù)組返回。
顯然getAutoConfigurationEntry()是個重點。
往下翻一點,就是getAutoConfigurationEntry()的實現(xiàn),在這里打個斷點(把上面的斷點取消掉)。

debug重新運行一下,往下走到getCandidateConfigurations()。

這里是獲取所有候選配置,目前可以看到這里是共有 127 個。

為什么是這 127 個?其實是在配置文件里寫死了,在 springboot 啟動時候,給容器加載的所有場景的配置類。
定義的位置是在這:\spring-boot-autoconfigure\2.3.4.RELEASE\spring-boot-autoconfigure-2.3.4.RELEASE.jar!\META-INF\spring.factories

雖然這些一股腦的在啟動時候會去加載到容器,但是最終會按需開啟配置。

比如點開aop,看到@ConditionalOnClass({Advice.class})這個條件,是當存在Advice類時候才導入組件,但實際上這里并沒有Advice。
這就是基于 springboot 的按條件裝配@Conditional,根據(jù)規(guī)則最終實現(xiàn)按需裝配。
二、自動配置示例
分別用最終未生效、和生效的自動配置來加深理解。
1. 未生效的自動配置
比如 cache。

可以看到CacheAutoConfiguration上是加了幾個條件裝配的。

(1)@ConditionalOnClass({CacheManager.class})
在 idea 中使用ctrl+N搜索一下CacheManager,發(fā)現(xiàn)是存在的,那么這個條件滿足。

(2)@ConditionalOnBean({CacheAspectSupport.class})
這個條件是要求容器中存在CacheAspectSupport這個組件才可以。
現(xiàn)在來判斷一下是否存在這個組件,在 main 方法里增加測試代碼:
... ...
String[] beanNamesForType = run.getBeanNamesForType(CacheAspectSupport.class);
System.out.println("==CacheAspectSupport類型組件的數(shù)量==" + beanNamesForType.length);
... ...運行查看輸出。

發(fā)現(xiàn)數(shù)量等于 0,也就是不存在該類型的組件。
也就是說@ConditionalOnBean({CacheAspectSupport.class})這個條件不滿足,所以整個類CacheAutoConfiguration里的配置都不生效。
2. 生效的自動配置
之前寫過 web 的demo,那么 web 相關(guān)的配置自然是生效的,找到它。

這里有不少后綴是**AutoConfiguration的配置,直接來看DispatcherServletAutoConfiguration。

- @Configuration(proxyBeanMethods = false):表示是一個配置類。
- @ConditionalOnWebApplication(type = Type.SERVLET):條件是否為一個 web 應(yīng)用,而且是原生 SERVLET 類型的(因為springboot2還有webflux),當前滿足條件。
- @ConditionalOnClass({DispatcherServlet.class}):條件是否導入了DispatcherServlet類,這里也是有的。

還有 2 個注解直接沒見過,這里不用太多關(guān)注,了解一下:
- @AutoConfigureOrder:這個配置類的配置優(yōu)先級順序。
- @AutoConfigureAfter:表示在xx之后才配置這個類,這里就是在配置完ServletWebServerFactoryAutoConfiguration.class后,再配置當前的類。
所以,類上的幾個條件都是滿足的,就可以進一步到類中了,繼續(xù)往下找:

看到DispatcherServletConfiguration類上也有條件:
@Conditional({DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition.class}):別看這么長,其實就是上面的一個類
@ConditionalOnClass({ServletRegistration.class}): 這個也存在。

@EnableConfigurationProperties({WebMvcProperties.class}):這個很熟悉了,使用前面剛學習完不久,它并不是條件裝配,而是用來綁定外部配置文件的,點進去。

可以看到,會與配置文件中前綴是spring.mvc的所有屬性進行綁定。
另外,還可以自動把組件注冊到容器中去。
這里可以試一下,在 main 方法里增加輸出:
String[] beanNamesForType1 = run.getBeanNamesForType(WebMvcProperties.class);
System.out.println("==WebMvcProperties類型組件的數(shù)量==" + beanNamesForType1.length);運行一下,果然是有一個:

到此,說明DispatcherServletConfiguration這個配置類也是生效的。
繼續(xù)往下就看到方法dispatcherServlet(),而且是加了@Bean注解,就是給容器中注冊DispatcherServlet類型的組件。

這里的經(jīng)過是:
- new 一個
DispatcherServlet()對象dispatcherServlet。 - 接著對
dispatcherServlet一通 set 設(shè)置。 - 最后返回這個對象
dispatcherServlet。
在之前學習 springMVC 時候,還要手動去設(shè)置關(guān)于DispatcherServlet的一堆東西。而在 springboot 里已經(jīng)在底層設(shè)置好了,并且注冊到容器中去了,所以我們能直接使用。
三、小結(jié)
隨著進一步跟著源碼來理解自動配置的原理,使得自己更深的體會到 springboot 的優(yōu)點。
那么多東西不需要我們手動去配置了,并不是說用不上,而是在底層springboot已經(jīng)幫我們完成好了配置。
當然,目前的重點還是學會使用 springboot,但是帶著之前對 springboot 的疑問來學習,還是更有收獲的。
以上就是SpringBoot2入門自動配置原理及源碼分析的詳細內(nèi)容,更多關(guān)于SpringBoot2自動配置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于SpringCloud灰度發(fā)布的實現(xiàn)
這篇文章主要介紹了關(guān)于SpringCloud灰度發(fā)布的實現(xiàn),灰度發(fā)布又稱金絲雀發(fā)布,是在系統(tǒng)升級的時候能夠平滑過渡的一種發(fā)布方式,灰度發(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時候就可以發(fā)現(xiàn)、調(diào)整問題,以保證其影響度,需要的朋友可以參考下2023-08-08
關(guān)于feign對x-www-form-urlencode類型的encode和decode問題
這篇文章主要介紹了關(guān)于feign對x-www-form-urlencode類型的encode和decode問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Java基礎(chǔ)之淺談hashCode()和equals()
今天給大家?guī)淼氖顷P(guān)于Java基礎(chǔ)的相關(guān)知識,文章圍繞著hashCode()和equals()展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Java并發(fā)系列之AbstractQueuedSynchronizer源碼分析(條件隊列)
這篇文章主要為大家詳細介紹了Java并發(fā)系列之AbstractQueuedSynchronizer源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-02-02

