每次問到 Spring Boot蔬咬, 面試官非常喜歡問這個問題:“講述一下 SpringBoot 自動裝配原理粤咪?”谚中。
我覺得我們可以從以下幾個方面回答:
什么是 SpringBoot 自動裝配?
SpringBoot 是如何實現(xiàn)自動裝配的寥枝?如何實現(xiàn)按需加載宪塔?
如何實現(xiàn)一個 Starter?
前言
使用過 Spring 的小伙伴囊拜,一定有被 XML 配置統(tǒng)治的恐懼某筐。即使 Spring 后面引入了基于注解的配置,我們在開啟某些 Spring 特性或者引入第三方依賴的時候冠跷,還是需要用 XML 或 Java 進行顯式配置南誊。
舉個例子。沒有 Spring Boot 的時候蜜托,我們寫一個 RestFul Web 服務(wù)抄囚,還首先需要進行如下配置。
spring-servlet.xml
但是橄务,Spring Boot 項目幔托,我們只需要添加相關(guān)依賴,無需配置蜂挪,通過啟動下面的main 方法即可重挑。
并且,我們通過 Spring Boot 的全局配置文件 application.properties或application.yml即可對項目進行設(shè)置比如更換端口號棠涮,配置 JPA 屬性等等攒驰。
為什么 Spring Boot 使用起來這么酸爽呢??這得益于其自動裝配故爵。自動裝配可以說是 Spring Boot 的核心,那究竟什么是自動裝配呢?
什么是 SpringBoot 自動裝配诬垂?
我們現(xiàn)在提到自動裝配的時候劲室,一般會和 Spring Boot 聯(lián)系在一起。但是结窘,實際上 Spring Framework 早就實現(xiàn)了這個功能很洋。Spring Boot 只是在其基礎(chǔ)上,通過 SPI 的方式隧枫,做了進一步優(yōu)化喉磁。
SpringBoot 定義了一套接口規(guī)范,這套規(guī)范規(guī)定:SpringBoot 在啟動時會掃描外部引用 jar 包中的META-INF/spring.factories文件官脓,將文件中配置的類型信息加載到 Spring 容器(此處涉及到 JVM 類加載機制與 Spring 的容器知識)协怒,并執(zhí)行類中定義的各種操作。對于外部 jar 來說卑笨,只需要按照 SpringBoot 定義的標準孕暇,就能將自己的功能裝置進 SpringBoot。
沒有 Spring Boot 的情況下赤兴,如果我們需要引入第三方依賴妖滔,需要手動配置,非常麻煩桶良。但是座舍,Spring Boot 中,我們直接引入一個 starter 即可陨帆。比如你想要在項目中使用 redis 的話曲秉,直接在項目中引入對應(yīng)的 starter 即可。
引入 starter 之后歧譬,我們通過少量注解和一些簡單的配置就能使用第三方組件提供的功能了岸浑。
在我看來,自動裝配可以簡單理解為:通過注解或者一些簡單的配置就能在 Spring Boot 的幫助下實現(xiàn)某塊功能瑰步。
SpringBoot 是如何實現(xiàn)自動裝配的矢洲?
我們先看一下 SpringBoot 的核心注解 SpringBootApplication 。
大概可以把 @SpringBootApplication看作是 @Configuration缩焦、@EnableAutoConfiguration读虏、@ComponentScan 注解的集合。根據(jù) SpringBoot 官網(wǎng)袁滥,這三個注解的作用分別是:
@EnableAutoConfiguration:啟用 SpringBoot 的自動配置機制
@Configuration:允許在上下文中注冊額外的 bean 或?qū)肫渌渲妙?/p>
@ComponentScan:掃描被@Component (@Service,@Controller)注解的 bean盖桥,注解默認會掃描啟動類所在的包下所有的類 ,可以自定義不掃描某些 bean题翻。如下圖所示揩徊,容器中將排除TypeExcludeFilter和AutoConfigurationExcludeFilter。
@EnableAutoConfiguration 是實現(xiàn)自動裝配的重要注解,我們以這個注解入手塑荒。
@EnableAutoConfiguration:實現(xiàn)自動裝配的核心注解
EnableAutoConfiguration 只是一個簡單地注解熄赡,自動裝配核心功能的實現(xiàn)實際是通過 AutoConfigurationImportSelector類。
我們現(xiàn)在重點分析下AutoConfigurationImportSelector 類到底做了什么齿税?
AutoConfigurationImportSelector:加載自動裝配類
AutoConfigurationImportSelector類的繼承體系如下:
可以看出彼硫,AutoConfigurationImportSelector 類實現(xiàn)了 ImportSelector接口,也就實現(xiàn)了這個接口中的 selectImports方法凌箕,該方法主要用于獲取所有符合條件的類的全限定類名拧篮,這些類需要被加載到 IoC 容器中。
這里我們需要重點關(guān)注一下getAutoConfigurationEntry()方法牵舱,這個方法主要負責(zé)加載自動配置類的串绩。
該方法調(diào)用鏈如下:
現(xiàn)在我們結(jié)合getAutoConfigurationEntry()的源碼來詳細分析一下:
第 1 步:
判斷自動裝配開關(guān)是否打開。默認spring.boot.enableautoconfiguration=true仆葡,可在 application.properties 或 application.yml 中設(shè)置
第 2 步?:
用于獲取EnableAutoConfiguration注解中的 exclude 和 excludeName赏参。
第 3 步
獲取需要自動裝配的所有配置類,讀取META-INF/spring.factories
從下圖可以看到這個文件的配置內(nèi)容都被我們讀取到了沿盅。XXXAutoConfiguration的作用就是按需加載組件把篓。
不光是這個依賴下的META-INF/spring.factories被讀取到,所有 Spring Boot Starter 下的META-INF/spring.factories都會被讀取到腰涧。
所以韧掩,你可以清楚滴看到, druid 數(shù)據(jù)庫連接池的 Spring Boot Starter 就創(chuàng)建了META-INF/spring.factories文件窖铡。
如果疗锐,我們自己要創(chuàng)建一個 Spring Boot Starter,這一步是必不可少的费彼。
第 4 步?:
到這里可能面試官會問你:“spring.factories中這么多配置滑臊,每次啟動都要全部加載么?”箍铲。
很明顯雇卷,這是不現(xiàn)實的。我們 debug 到后面你會發(fā)現(xiàn)颠猴,configurations 的值變小了关划。
因為,這一步有經(jīng)歷了一遍篩選翘瓮,@ConditionalOnXXX 中的所有條件都滿足贮折,該類才會生效。
有興趣的童鞋可以詳細了解下 Spring Boot 提供的條件注解
@ConditionalOnBean:當容器里有指定 Bean 的條件下
@ConditionalOnMissingBean:當容器里沒有指定 Bean 的情況下
@ConditionalOnSingleCandidate:當指定 Bean 在容器中只有一個资盅,或者雖然有多個但是指定首選 Bean
@ConditionalOnClass:當類路徑下有指定類的條件下
@ConditionalOnMissingClass:當類路徑下沒有指定類的條件下
@ConditionalOnProperty:指定的屬性是否有指定的值
@ConditionalOnResource:類路徑是否有指定的值
@ConditionalOnExpression:基于 SpEL 表達式作為判斷條件
@ConditionalOnJava:基于 Java 版本作為判斷條件
@ConditionalOnJndi:在 JNDI 存在的條件下差在指定的位置
@ConditionalOnNotWebApplication:當前項目不是 Web 項目的條件下
@ConditionalOnWebApplication:當前項目是 Web 項 目的條件下
如何實現(xiàn)一個 Starter
光說不練假把式调榄,現(xiàn)在就來擼一個 starter踊赠,實現(xiàn)自定義線程池
第一步,創(chuàng)建threadpool-spring-boot-starter工程
第二步每庆,引入 Spring Boot 相關(guān)依賴
第三步臼疫,創(chuàng)建ThreadPoolAutoConfiguration
第四步,在threadpool-spring-boot-starter工程的 resources 包下創(chuàng)建META-INF/spring.factories文件
最后新建工程引入threadpool-spring-boot-starter
測試通過?勖稀!荣赶!
總結(jié)
Spring Boot 通過@EnableAutoConfiguration開啟自動裝配凤价,通過 SpringFactoriesLoader 最終加載META-INF/spring.factories中的自動配置類實現(xiàn)自動裝配,自動配置類其實就是通過@Conditional按需加載的配置類拔创,想要其生效必須引入spring-boot-starter-xxx包實現(xiàn)起步依賴