文末送書 | 帶你搞定 Spring Boot 核心運(yùn)行原理
前言
Spring Boot 最核心的功能就是“自動(dòng)配置”,這一切都基于“約定優(yōu)于配置”的原則。那么 Spring Boot 是如何約定,又是如何實(shí)現(xiàn)自動(dòng)配置功能的呢?
下面帶大家來通過源碼學(xué)習(xí) Spring Boot 的核心運(yùn)作原理以及最核心的注解 @EnableAutoConfiguration 來進(jìn)行講解。
1.1 核心運(yùn)行原理
使用 Spring Boot 時(shí)我們只需引入對應(yīng)的 Starters,Spring Boot 啟動(dòng)時(shí)便自會(huì)動(dòng)加載相關(guān)依賴,配置相應(yīng)的初始化參數(shù),以最快捷、簡單的形式對第三方軟件進(jìn)行集成,這便是 Spring Boot 的自動(dòng)配置功能。先從整體上看一下 Spring Boot 實(shí)現(xiàn)該運(yùn)作機(jī)制涉及到的核心部分,如下圖。

圖中描述了 Spring Boot 自動(dòng)配置運(yùn)作過程中涉及的幾個(gè)核心功能及其相互之間的關(guān)系。
它們包括:@EnableAutoConfiguration、spring.factories、各組件對應(yīng)的 AutoConfiguration 類、@Conditional 注解以及各種 Starters。
如果用一句話來描述整個(gè)過程就是:Spring Boot 通過 @EnableAutoConfiguration 注解開啟自動(dòng)配置,加載 spring.factories 中注冊的各種 AutoConfiguration 類,當(dāng)某個(gè) AutoConfiguration 類滿足其注解 @Conditional 指定的生效條件(Starters 提供的依賴、配置或 Spring 容器中是否存在某個(gè) Bean 等)時(shí),那么實(shí)例化該 AutoConfiguration 類中定義的 Bean(組件等),并注入 Spring 容器,至此就完成了依賴框架的自動(dòng)配置。
先從概念及功能上了解一下上圖所屬部分的作用及相互關(guān)系。
@EnableAutoConfiguration:該注解由組合注解 @SpringBootApplication 引入,完成自動(dòng)配置開啟,掃描各個(gè)jar包下的 spring.factories 文件,并加載其中注冊的 AutoConfiguration 類等。
spring.factories:配置文件,位于 jar 包的META-INF目錄下,按照指定格式注冊了自動(dòng)配置的 AutoConfiguration 類,也可以包含其他類型待注冊的類。該配置文件不僅存在于 Spring Boot 項(xiàng)目中,也可以存在于自定義的自動(dòng)配置(或Starter)項(xiàng)目中。
AutoConfiguration 類:自動(dòng)配置類,代表了 Spring Boot 中一類以XXAutoConfiguration命名的自動(dòng)配置類,其中定義了三方組件集成 Spring 所需初始化的 Bean 和條件。
@Conditional:條件注解及其衍生注解,使用在 AutoConfiguration 類上,當(dāng)滿足該條件注解時(shí)才會(huì)實(shí)例化AutoConfiguration類。
Starters:三方組件的依賴及配置,Spring Boot 已經(jīng)預(yù)置的組件。Spring Boot 默認(rèn)的 Starters 項(xiàng)目往往是只包含了一個(gè)pom依賴的項(xiàng)目。如果自定義的 starter,該項(xiàng)目還需包含spring.factories文件、AutoConfiguration類和其他配置類。
以上在概念層面了解了 Spring Boot 自動(dòng)配置的整體流程和基本運(yùn)作原理,下面重點(diǎn)看一下 @EnableAutoConfiguration注解的功能。
1.2 運(yùn)作原理源碼解析之 @EnableAutoConfiguration
@EnableAutoConfiguration 是開啟自動(dòng)配置的注解,在創(chuàng)建的 Spring Boot 項(xiàng)目中并不能直接看到此注解,它是由組合注解@SpringBootApplication引入。下面,我們首先了解一下入口類和@SpringBootApplication注解,然后再深入了解@EnableAutoConfiguration注解的構(gòu)成與作用。
1.2.1入口類和 @SpringBootApplication注解
Spring Boot 項(xiàng)目創(chuàng)建完成會(huì)默認(rèn)生成一個(gè)*Application的入口類。默認(rèn)情況下,無論通過 IDEA 或通過官網(wǎng)創(chuàng)建基于 Maven 的 Spring Boot 項(xiàng)目,入口類的命名規(guī)則為:artifactId+Application。通過該類的 main 方法即可啟動(dòng) Spring Boot 項(xiàng)目。
@SpringBootApplication
public?class?SpringLearnApplication?{
?public?static?void?main(String[]?args)?{
??????SpringApplication.run(DemoApplication.class,?args);
???}
}
關(guān)于 main 方法并無特別之處,就是一個(gè)標(biāo)準(zhǔn)的 Java 應(yīng)用的 main 方法,這里是啟動(dòng) Spring Boot 項(xiàng)目的入口。默認(rèn)情況下,按照上述命名規(guī)則并包含 main 方法的類,我們以下稱作“入口類”。
在入口類中,我們可以看到 Spring Boot 創(chuàng)建的代碼中(除單元測試外)唯一的一個(gè)注解就是@SpringBootApplication。它是 Spring Boot 項(xiàng)目的核心注解,用于開啟自動(dòng)配置,準(zhǔn)確說是該注解內(nèi)組合的@EnableAutoConfiguration開啟了自動(dòng)配置。
@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?{
???//?排除指定自動(dòng)配置類
???@AliasFor(annotation?=?EnableAutoConfiguration.class)
???Class>[]?exclude()?default?{};
//?排除指定自動(dòng)配置類名
???@AliasFor(annotation?=?EnableAutoConfiguration.class)
???String[]?excludeName()?default?{};
//?指定掃描的基礎(chǔ)包,激活注解組件的初始化
???@AliasFor(annotation?=?ComponentScan.class,?attribute?=?"basePackages")
???String[]?scanBasePackages()?default?{};
//?指定掃描的類,用于初始化
???@AliasFor(annotation?=?ComponentScan.class,?attribute?=?"basePackageClasses")
???Class>[]?scanBasePackageClasses()?default?{};
//?指定是否代理@Bean方法以強(qiáng)制執(zhí)行bean的聲明周期行為。
@AliasFor(annotation?=?Configuration.class)
boolean?proxyBeanMethods()?default?true;
}
通過源代碼可以看出,該注解提供了以下成員屬性(注解中的成員變量以方法的形式體現(xiàn)):
exclude:根據(jù)類(Class)排除指定的自動(dòng)配置,該成員屬性覆蓋了@SpringBootApplication 中組合的@EnableAutoConfiguration中定義的exclude成員屬性。
excludeName:根據(jù)類名排除指定的自動(dòng)配置,同樣覆蓋了@EnableAutoConfiguration中的excludeName的成員屬性。
scanBasePackages:指定掃描的基礎(chǔ) package,用于激活@Component等注解類的初始化。
scanBasePackageClasses:掃描指定的類,用于組件的初始化。
proxyBeanMethods:指定是否代理@Bean方法以強(qiáng)制執(zhí)行 bean 的聲明周期行為。此功能需要通過運(yùn)行時(shí)生成CGLIB子類來實(shí)現(xiàn)方法攔截,該子類有一定的限制,比如配置類及其方法不允許聲明為 final 等。默認(rèn)值為true,允許配置類中進(jìn)行“inter-bean references”(bean之間的引用)以及對該配置的@Bean方法的外部調(diào)用。如果@Bean方法都是自包含的,并且僅提供了容器使用的普通工程方法的功能,則可設(shè)置為false,避免處理CGLIB子類。Spring Boot 2.2新增該成員屬性,自動(dòng)配置類中基本都會(huì)使用,一般情況下都配置為false。
以上源代碼會(huì)發(fā)現(xiàn),大量使用了@AliasFor注解,該注解用于橋接到其他注解,該注解的屬性中指定了所橋接的注解類。如果點(diǎn)進(jìn)去查看,會(huì)發(fā)現(xiàn)@SpringBootApplication定義的屬性,在其他注解中已經(jīng)定義過了。之所以使用@AliasFor注解并重新在@SpringBootApplication中定義,更多的意義是為了減少用戶使用多注解帶來的麻煩。
@SpringBootApplication注解中組合了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。因此,在實(shí)踐過程中也可以使用這三個(gè)注解來替代@SpringBootApplication。
在早期版本中并沒有@SpringBootConfiguration注解,后新增了@SpringBootConfiguration并在其內(nèi)組合了@Configuration。@EnableAutoConfiguration注解組合了@AutoConfigurationPackage。忽略掉一些基礎(chǔ)注解和元注解,@SpringBootApplication注解的組合結(jié)構(gòu)可以參考下圖。

在上圖中,@SpringBootApplication除了組合元注解之外,核心包括:用于激活Spring Boot自動(dòng)配置的@EnableAutoConfiguration、用于激活@Component掃描的@ComponentScan、用于激活配置類的@Configuration。其@ComponentScan注解和@Configuration注解在Spring的日常使用中經(jīng)常用到,不再贅述,而關(guān)于@EnableAutoConfiguration正是我們下面要詳細(xì)介紹的。
1.2.2 注解@EnableAutoConfiguration功能解析
在未使用Spring Boot的情況下,Bean的生命周期由Spring來管理,然而Spring無法自動(dòng)配置@Configuration注解的類。而Spring Boot所做的核心之一就是根據(jù)約定可以自動(dòng)化的管理該注解標(biāo)注的類。用來實(shí)現(xiàn)該功能的組件之一便是@EnableAutoConfiguration注解。
@EnableAutoConfiguration位于spring-boot-autoconfigure包內(nèi),當(dāng)使用@SpringBootApplication注解時(shí),該注解會(huì)自動(dòng)生效。
@EnableAutoConfiguration的主要功能是啟動(dòng)Spring應(yīng)用程序上下文時(shí)進(jìn)行自動(dòng)配置,它會(huì)嘗試猜測并配置你可能需要的Bean。自動(dòng)配置通常是基于項(xiàng)目classpath中引入的類和已定義的Bean來實(shí)現(xiàn)的。在此過程中,被自動(dòng)配置的組件來自項(xiàng)目自身和項(xiàng)目依賴的jar包中。
舉個(gè)例子:如果將tomcat-embedded.jar添加到classpath下,那么@EnableAutoConfiguration會(huì)認(rèn)為你準(zhǔn)備使用TomcatServletWebServerFactory類,并幫你初始化相關(guān)配置。與此同時(shí)如果自定義了基于ServletWebServerFactory的 Bean,那么,它將不會(huì)進(jìn)行TomcatServletWebServerFactory類的初始化。這一系列的操作判斷,都由 Spring Boot 來完成。
下面我們來看一下@EnableAutoConfiguration注解的源代碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public?@interface?EnableAutoConfiguration?{
//?用來覆蓋配置來開啟/關(guān)閉自動(dòng)配置的功能
???String?ENABLED_OVERRIDE_PROPERTY?=?"spring.boot.enableautoconfiguration";
//?根據(jù)類(Class)排除指定的自動(dòng)配置
???Class>[]?exclude()?default?{};
//?根據(jù)類名排除指定的自動(dòng)配置
???String[]?excludeName()?default?{};
}
該注解提供了一個(gè)常量和兩個(gè)成員參數(shù)的定義:
ENABLED_OVERRIDE_PROPERTY:可以用來覆蓋配置來開啟/關(guān)閉自動(dòng)配置的功能。
exclude:根據(jù)類(Class)排除指定的自動(dòng)配置
excludeName:根據(jù)類名排除指定的自動(dòng)配置。
正如上面所說,@EnableAutoConfiguration會(huì)猜測你所需要使用的Bean,但如果在實(shí)戰(zhàn)中你并不需要它預(yù)置初始化的Bean,則可通過該注解的exclude或excludeName參數(shù)來進(jìn)行有針對性的排除。
比如,當(dāng)不需要數(shù)據(jù)庫的自動(dòng)配置時(shí),可通過以下兩種方式讓其自動(dòng)配置失效:
//?通過@SpringBootApplication排除DataSourceAutoConfiguration
@SpringBootApplication(exclude?=?DataSourceAutoConfiguration.class)?
public?class?SpringLearnApplication?{
}
或:
//?通過@EnableAutoConfiguration排除DataSourceAutoConfiguration
@Configuration
@EnableAutoConfiguration(exclude?=?DataSourceAutoConfiguration.class)
public?class?DemoConfiguration?{
}
需要注意的是,被@EnableAutoConfiguration注解的類所在 package 還具有特定的意義,通常會(huì)被作為掃描注解@Entity的根路徑。這也是為什么在使用@SpringBootApplication注解時(shí)需要將被注解的類放在頂級 package 下的原因,如果放在較低層級,則它所在 package 的同級或上級中的類則無法被掃描到。
而對于入口類和其main方法來說,并不依賴@SpringBootApplication注解或@EnableAutoConfiguration,也就是說該注解可以使用在其他類上,而非入口類上,同樣生效。
1.3 AutoConfigurationImportSelector源碼解析
@EnableAutoConfiguration的關(guān)鍵功能是通過@Import注解導(dǎo)入的ImportSelector來完成的。從源碼得知@Import(AutoConfigurationImportSelector.class)是@EnableAutoConfiguration注解的組成部分,也是自動(dòng)配置功能的核心實(shí)現(xiàn)者。
它又可以分為兩部分:
@Import和對應(yīng)的ImportSelector。本節(jié)重點(diǎn)講解@Import的基本使用和ImportSelector的實(shí)現(xiàn)類AutoConfigurationImportSelector。
1.3.3.AutoConfigurationImportSelector功能概述
下面,通過一張流程圖來從整體上了解?AutoConfigurationImportSelector?功能及流程圖。
圖中省略了外部通過@Import注解調(diào)用該類的部分。

當(dāng)?AutoConfigurationImportSelector?被?@Import?注解引入之后,它的?selectImports?方法會(huì)被調(diào)用并執(zhí)行其實(shí)現(xiàn)的自動(dòng)裝配邏輯。讀者朋友需注意的?selectImports?方法幾乎涵蓋了組件自動(dòng)裝配的所有處理邏輯。
通過上圖從整體上了解了?AutoConfigurationImportSelector?的概況及操作流程,關(guān)于?AutoConfigurationImportSelector?的詳細(xì)實(shí)現(xiàn)可參考Spring Boot中的源碼進(jìn)行逐步分析,本篇文章就不再進(jìn)行過多拓展。
1.4 小結(jié)
本篇文章圍繞 Spring Boot 的核心功能展開,帶大家從整體上了解 Spring Boot 自動(dòng)配置的原理以及自動(dòng)配置核心組件的運(yùn)作過程。只有掌握了這些基礎(chǔ)的組建及其功能,在后續(xù)集成其他三方類庫的自動(dòng)配置時(shí)才能夠更加清晰的了解它們都運(yùn)用了自動(dòng)配置的哪些功能。
以上摘自朱智勝《Spring Boot技術(shù)內(nèi)幕——架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)原理》。
為了感謝大家一直以來對武培軒的支持,此次我準(zhǔn)備了3本《Spring Boot技術(shù)內(nèi)幕——架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)原理》送給大家~
參與方式
?方式一:公眾號【武培軒】回復(fù)【送書】,贈(zèng)書兩本
?方式二:到截止日期為止,分享榜第一名,贈(zèng)書一本
特別提醒:方式一、二可同時(shí)參與(獲獎(jiǎng)幾率更大~),活動(dòng)截止日期8月12日下午6點(diǎn)
獲獎(jiǎng)公布
公布時(shí)間:8月13日次條
特別提醒:兌獎(jiǎng)截止至8月20日,請參與讀者及時(shí)兌獎(jiǎng)
完
? ? ? ?
???覺得不錯(cuò),點(diǎn)個(gè)在看~

