最近有個讀者在面試,面試中被問到了這樣一個問題「看你項目中用到了springboot
,你說下springboot
的自動配置是怎麼實現的?」這應該是一個springboot
裏面最最多見的一個面試題了。下面咱們就來帶着這個問題一塊兒解剖下springBoot
的自動配置原理吧。html
首先咱們回顧下原來搭建一個springmvc
的hello-word
的web
項目(xml
配置的)咱們是否是要在pom
中導入各類依賴,而後各個依賴有可能還會存在版本衝突須要各類排除。當你歷盡千辛萬苦的把依賴解決了,而後還須要編寫web.xml、springmvc.xml
配置文件等。咱們只想寫個hello-word
項目而已,確把一大把的時間都花在了配置文件和jar
包的依賴上面。大大的影響了咱們開發的效率,以及加大了web
開發的難度。爲了簡化這複雜的配置、以及各個版本的衝突依賴關係,springBoot
就應運而生。咱們如今經過idea
建立一個springboot
項目只要分分鐘就解決了,你不須要關心各類配置(基本實現零配置)。讓你真正的實現了開箱即用。SpringBoot
幫你節約了大量的時間去陪女友,不對程序員怎麼會有女友呢?(沒有的話也是能夠new一個的)它的出現不只可讓你把更多的時間都花在你的業務邏輯開發上,並且還大大的下降了web
開發的門檻。因此SpringBoot
仍是比較善解人衣的,錯啦錯啦是善解人意,知道開發人員的痛點在哪。
java
既然Springboot
儘管這麼好用,可是做爲一個使用者,咱們仍是比較好奇它是怎麼幫咱們實現開箱即用的。Spring Boot
有一個全局配置文件:application.properties或application.yml
。在這個全局文件裏面能夠配置各類各樣的參數好比你想改個端口啦server.port
或者想調整下日誌的級別啦統統均可以配置。更多其餘能夠配置的屬性能夠參照官網。https://docs.spring.io/spring...
![
](https://img-blog.csdnimg.cn/2...
這麼多屬性,這些屬性在項目是怎麼起做用的呢?SpringBoot
項目看下來啥配置也沒有,配置」(application.properties或application.yml
除外),既 然從配置上面找不到突破口,那麼咱們就只能從啓動類上面找入口了。啓動類也就一個光禿禿的一個main
方法,類上面僅有一個注SpringBootApplication
這個註解是Spring Boot
項目必不可少的註解。那麼自動配置原理必定和這個註解有着千絲萬縷的聯繫!咱們下面來一塊兒看看這個註解吧。
@SpringBootApplication註解程序員
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
這裏最上面四個註解的話沒啥好說的,基本上本身實現過自定義註解的話,都知道分別是什麼意思。web
@SpringBootConfiguration
繼承自@Configuration
,兩者功能也一致,標註當前類是配置類。@ComponentScan
用於類或接口上主要是指定掃描路徑,跟Xml裏面的<context:component-scan base-package="" />
配置同樣。springboot
若是不寫這個掃描路徑的話,默認就是啓動類的路徑。@EnableAutoConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
這個註解咱們重點看下AutoConfigurationImportSelector
這個類getCandidateConfigurations
這個方法裏面經過SpringFactoriesLoader.loadFactoryNames()
掃描全部具備META-INF/spring.factories
的jar
包( spring.factories 咱們能夠理解成 Spring Boot
本身的 SPI
機制)。spring-boot-autoconfigure-x.x.x.x.jar
裏就有一個spring.factories文件。spring.factories
文件由一組一組的Key = value
的形式,其中一個key
是EnableAutoConfiguration類的全類名,而它的value是一個以AutoConfiguration
結尾的類名的列表,有redis、mq
等這些類名以逗號分隔。
面試
咱們在回到getAutoConfigurationEntry
這個方法當執行完getCandidateConfigurations
這個方法的時候咱們能夠看到此時總共加載了127
個自動配置類。
這些類難道都要加載進去嗎?springboot
仍是沒有那麼傻的,它提倡的話是按需加載。redis
exclude
註解的類下面配置就會過濾掉RestTemplateAutoConfiguration
這個類
spring boot
是經過條件註解來實現的。@ConditionalOnBean:當容器裏有指定Bean的條件下
@ConditionalOnClass:當類路徑下有指定的類的條件下
@ConditionalOnExpression:基於SpEL表達式爲true的時候做爲判斷條件纔去實例化
@ConditionalOnJava:基於JVM版本做爲判斷條件
@ConditionalOnJndi:在JNDI存在的條件下查找指定的位置
@ConditionalOnMissingBean:當容器裏沒有指定Bean的狀況下
@ConditionalOnMissingClass:當容器裏沒有指定類的狀況下
@ConditionalOnWebApplication:當前項目時Web項目的條件下
@ConditionalOnNotWebApplication:當前項目不是Web項目的條件下
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑是否有指定的值
@ConditionalOnOnSingleCandidate:當指定Bean在容器中只有一個,或者有多個可是指定首選的Bean
這些註解都組合了@Conditional
註解,只是使用了不一樣的條件組合最後爲true時纔會去實例化須要實例化的類,不然忽略過濾掉。咱們在回到代碼能夠看到通過了條件判斷過濾後咱們剩下符合條件的自動配置類只剩23個了。其餘的都是由於不知足條件註解而被過濾了。
若是咱們想知道哪些自動配置類被過濾了,是因爲什麼緣由被過濾了,以及加載了哪些類等。spring boot
都爲咱們記錄了日誌。仍是很是貼心的。咱們能夠調整下咱們日誌的級別改成debug
。而後咱們就能看到如下日誌了
這裏就截取了部分日誌。總共分別有下面四部分日誌:spring
Positive matches
:@Conditional
條件爲真,配置類被Spring容器加載。Negative matches:
@Conditional
條件爲假,配置類未被Spring容器加載。Exclusions
: 咱們明確了不須要加載的類。好比在上面啓動類配置的RestTemplateAutoConfiguration
類Unconditional classes
: 自動配置類不包含任何類級別的條件,也就是說,類始終會被自動加載。咱們以ServletWebServerFactoryAutoConfiguration
配置類爲例,解釋一下全局配置文件中的屬性如何生效,好比:server.port=88
,是如何生效的(固然不配置也會有默認值,這個默認值來自於org.apache.catalina.startup.Tomcat
)。apache
// 標記爲配置類 @Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 若是有ServletRequest.class 纔會生效 @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) // 把@ConfigurationProperties註解的類注入爲Spring容器的Bean。 @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration {
咱們能夠發現EnableConfigurationProperties
註解裏面配置的ServerProperties.class
springboot
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { /** * Server HTTP port. */ private Integer port;
在這個類上有一個註解:@ConfigurationProperties
,它的做用就是從配置文件中綁定屬性到對應的bean上(也就是把咱們application.properties
對應的server.port映射到ServerProperties
類中的port
屬性)而@EnableConfigurationProperties
這個註解就是把已經綁定了屬性的bean
(ServerProperties
)注入到spring
容器中(至關於@Component
註解同樣)。
全部在配置文件中能配置的屬性都是在xxxxPropertites
類中封裝着,配置文件能配置什麼就能夠參照某個功能對應的這個屬性類。
到如今爲止應該能回答文章開頭的那個問題了,面試的時候應該不須要回答的這麼詳細能夠參考下如下答案:mvc
Spring Boot啓動的時候會經過@EnableAutoConfiguration註解找到META-INF/spring.factories配置文件中的全部自動配置類,並對其進行加載,而這些自動配置類都是以AutoConfiguration結尾來命名的,它實際上就是一個JavaConfig形式的Spring容器配置類,它能經過以Properties結尾命名的類中取得在全局配置文件中配置的屬性如:server.port,而XxxxProperties類是經過@ConfigurationProperties註解與全局配置文件中對應的屬性進行綁定的。
在網上找了一張圖,基本上把自動裝配的流程給說清楚了。
SpringBoot
啓動會加載大量的自動配置類(經過「SPI
」的方式),而後會根據條件註解保留一些須要的類。SpringBoot
基本實現了「零配置「,而且開箱即用。
站在巨人的肩膀上摘蘋果:
https://docs.spring.io/spring...
https://blog.csdn.net/u014745...
https://afoo.me/posts/2015-07...