阿里面試:看你springBoot用的比較溜來,說說springboot自動裝配是怎麼回事?

引言

最近有個讀者在面試,面試中被問到了這樣一個問題「看你項目中用到了springboot,你說下springboot的自動配置是怎麼實現的?」這應該是一個springboot裏面最最多見的一個面試題了。下面咱們就來帶着這個問題一塊兒解剖下springBoot的自動配置原理吧。html

SpringMvc和SpringBoot對比

首先咱們回顧下原來搭建一個springmvchello-wordweb項目(xml配置的)咱們是否是要在pom中導入各類依賴,而後各個依賴有可能還會存在版本衝突須要各類排除。當你歷盡千辛萬苦的把依賴解決了,而後還須要編寫web.xml、springmvc.xml配置文件等。咱們只想寫個hello-word項目而已,確把一大把的時間都花在了配置文件和jar包的依賴上面。大大的影響了咱們開發的效率,以及加大了web開發的難度。爲了簡化這複雜的配置、以及各個版本的衝突依賴關係,springBoot就應運而生。咱們如今經過idea建立一個springboot項目只要分分鐘就解決了,你不須要關心各類配置(基本實現零配置)。讓你真正的實現了開箱即用。SpringBoot幫你節約了大量的時間去陪女友,不對程序員怎麼會有女友呢?(沒有的話也是能夠new一個的)它的出現不只可讓你把更多的時間都花在你的業務邏輯開發上,並且還大大的下降了web開發的門檻。因此SpringBoot仍是比較善解人衣的,錯啦錯啦是善解人意,知道開發人員的痛點在哪。
在這裏插入圖片描述java

SpringBoot自動配置加載

既然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.factoriesjar包( 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。而後咱們就能看到如下日誌了
Positive matches:在這裏插入圖片描述
這裏就截取了部分日誌。總共分別有下面四部分日誌: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.classspringboot

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

    /**
     * Server HTTP port.
     */
    private Integer port;

在這個類上有一個註解:@ConfigurationProperties,它的做用就是從配置文件中綁定屬性到對應的bean上(也就是把咱們application.properties對應的server.port映射到ServerProperties 類中的port屬性)而@EnableConfigurationProperties這個註解就是把已經綁定了屬性的beanServerProperties)注入到spring容器中(至關於@Component註解同樣)。
全部在配置文件中能配置的屬性都是在xxxxPropertites類中封裝着,配置文件能配置什麼就能夠參照某個功能對應的這個屬性類。
到如今爲止應該能回答文章開頭的那個問題了,面試的時候應該不須要回答的這麼詳細能夠參考下如下答案:mvc

Spring Boot啓動的時候會經過@EnableAutoConfiguration註解找到META-INF/spring.factories配置文件中的全部自動配置類,並對其進行加載,而這些自動配置類都是以AutoConfiguration結尾來命名的,它實際上就是一個JavaConfig形式的Spring容器配置類,它能經過以Properties結尾命名的類中取得在全局配置文件中配置的屬性如:server.port,而XxxxProperties類是經過@ConfigurationProperties註解與全局配置文件中對應的屬性進行綁定的。

在網上找了一張圖,基本上把自動裝配的流程給說清楚了。
圖片來源https://afoo.me/posts/2015-07-09-how-spring-boot-works.html

總結

  • SpringBoot啓動會加載大量的自動配置類(經過「SPI」的方式),而後會根據條件註解保留一些須要的類。
  • 咱們新引入一個組件,能夠先看看springBoot是否已經有默認的提供。
  • SpringBoot基本實現了「零配置「,而且開箱即用。

結束

  • 因爲本身才疏學淺,不免會有紕漏,假如你發現了錯誤的地方,還望留言給我指出來,我會對其加以修正。
  • 若是你以爲文章還不錯,你的轉發、分享、讚揚、點贊、留言就是對我最大的鼓勵。
  • 感謝您的閱讀,十分歡迎並感謝您的關注。

在這裏插入圖片描述
站在巨人的肩膀上摘蘋果:
https://docs.spring.io/spring...
https://blog.csdn.net/u014745...
https://afoo.me/posts/2015-07...

相關文章
相關標籤/搜索