最通俗易懂,最詳細的springboot自動配置原理解析
tips:
從springboot的入門案例中,我們可以體會到springboot的便捷之處,使用Spring Initializer建立一個專案,然後寫一個controller層就可以執行起來,我們啥也沒配置,沒配置tomcat、沒配置mvc、沒配置spring。。。。因為springboot底層都幫我們配置好了,而springboot的精髓就在於自動配置
然後不得不提一下springboot的四大特性:
- 自動裝配
- Starter新增專案依賴
- Spring Boot CLI與Groovy的高效配合
- Spring Boot Actuator
本文主要講解前兩點(重點為自動裝配)
Starter新增專案依賴:
先從入門案例的pom.xml開始
springboot的專案中都會存在一個父依賴,如下
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
該父專案為所有spring-boot-starter
的父專案
父專案有什麼用?用來做依賴管理,託管子專案,按住Ctrl+滑鼠左鍵點進去一探究竟
點進去之後發現spring-boot-starter
的父專案,還存在一個依賴。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.2.RELEASE</version> </parent>
再次點進去spring-boot-dependencies
會發現下圖所示:
定義著所有依賴的版本和管理
所以我們能得出第一個結論:
spring-boot-dependencies
存放著SpringBoot的核心依賴,管理springboot應用裡面的所有依賴版本。所以以後引入一些SpringBoot依賴的時候,不需要指定版本,但是沒有在spring-boot-dependencies
當中管理的需要宣告版本號。
那麼這些依賴是如何導進來的呢?
再回到pom.xml中,pom.xml中還匯入瞭如下依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
怎麼理解spring-boot-starter-web
呢?
可以拆分為兩部分來看spring-boot-starter
和web
spring-boot-starter
:spring的場景啟動器
springboot將所有的功能場景都抽取出來,做成一個個starters(啟動器),只需要在專案中引入這些starters相關場景的所有依賴都會匯入進來,而且版本會自動控制。
Starters官方解釋為一系列依賴描述的組合,可以通過匯入這些Starters就會有相應的依賴
官網中:
有aop面向切面程式設計,amqp高階訊息佇列。。。需要啥導啥就完事了
再點進 spring-boot-starter-web
看看,聲明瞭以下依賴:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency> 做資料校驗的
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
原來是幫我們匯入了web模組正常執行所需要依賴的元件
這個比較簡單,只是開胃菜,我們再看下一個知識點
自動配置
每次啟動都要通過這個主程式來啟動,這個主程式上放了一個註解@SpringBootApplication
,此註解意為:標註在某個類上說明這個類是SpringBoot的主配置類,SpringBoot就應該執行這個類的main方法來啟動SpringBoot應用;是我們研究的重點!!!
@SpringBootApplication
public class Springboot01HelloworldQuick03Application {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldQuick03Application.class, args);
}
}
接下來的分析由概括到詳細,由淺到深。
先了解大概,再逐一詳細分析
點進@SpringBootApplication
,發現是一個組合註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration //表示這是一個springboot的配置類
@EnableAutoConfiguration //開啟自動配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
最重要兩個註解就是@SpringBootConfiguration
和 @EnableAutoConfiguration
先看@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
再看@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //自動導包
@Import(AutoConfigurationImportSelector.class) //自動配置匯入選擇
public @interface EnableAutoConfiguration {
...
}
大致研究的方向:
@SpringBootConfiguration:
springboot的配置類,標註在某個類上,表示這是一個springboot的配置類
再點進去看一看:
@Configuration 標註在某個類上,表示這是一個springboot的配置類
@Configuration:
這個註解我們比較熟悉,spring中的配置類,相當於之前的xml配置檔案
可以給容器中注入元件。。。以前配置檔案的功能都可以做
此註解 @Configuration 也可以點進去,發現也是一個元件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
這個簡單不做深究,後面為重點
@EnableAutoConfiguration:
顧名思義:開啟自動配置功能,這個註解一定和自動配置相關,點進去看一下
@AutoConfigurationPackage //自動導包
@Import(AutoConfigurationImportSelector.class) //自動配置匯入選擇
這兩個註解是我們研究的重點
@AutoConfigurationPackage:
點進此註解看一下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
...
}
@Import為spring的註解,匯入一個配置檔案,在springboot中為給容器匯入一個元件
匯入的元件由AutoConfigurationPackages.Registrar.class執行邏輯來決定
再次點進去看看Registrar.class
並且可以打個斷點,啟動springboot,觀察一下
然後可以計算一下new PackageImports(metadata).getPackageNames()
的值,
選中,右鍵
計算結果為com.liqiliang.springboot:
new PackageImport(metadata).getPackageName()
最後計算這個包名為com.liqiliang.springboot
所以可以得到如下結論:
@AutoConfigurationPackage這個註解本身的含義就是將主配置類(@SpringBootApplication標註的類)所在的包下面所有的元件都掃描到spring容器中,包名都是主配置類的
所以如果將HelloController放到com.liqiliang.springboot
包外面就掃描不到了,就會報錯
@EnableAutoConfiguration的另一個註解:
@Import(AutoConfigurationImportSelector.class) //自動配置匯入選擇
AutoConfigurationImportSelector 開啟自動配置類的導包的選擇器(匯入哪些元件的選擇器)
點進去看一下
這個類中存在方法可以幫我們獲取所有的配置
先做演示,再詳解
此位置打斷點,為了獲取configurations的值
然後重啟應用
一直往下執行,注意控制檯是否出現configurations的值,
點開configurations,陣列長度為127
注意看檔名,字尾全為 ***AutoConfiguration
將所有需要匯入的元件以全類名的方式返回,並新增到容器中
最終會給容器中匯入非常多的自動配置類(xxxAutoConfiguration),給容器中匯入這個場景需要的所有元件,並配置好這些元件
有了自動配置類,免去了我們手動編寫配置注入功能元件等工作
詳解:
所有的配置都存放在configurations中,
而這些配置都從getCandidateConfigurations方法中獲取,
這個getCandidateConfigurations方法是用來獲取候選的配置。
這個方法可以用來獲取所有候選的配置,那麼這些候選的配置又是從哪來的呢?
點進getCandidateConfigurations方法
getCandidateConfiguration方法返回的list,這個list其實是由loadFactoryNames返回的,loadFactoryNames方法執行的時候傳入了兩個引數,一個引數為getSpringFactoriesLoaderFactoryClass(),此引數是什麼?看下圖
然後看到了一個眼熟的詞 --->EnableAutoConfiguration,
再去loadFactoryNames方法中看看,看他帶了EnableAutoConfiguration.class這個值做了什麼壞事情
先是將EnableAutoConfiguration.class傳給了factoryType,然後.getName( ),所以factoryTypeName值為EnableAutoConfiguration
然後呼叫loadSpringFactories( )方法,並且從類路徑下得到一個資源
FACTORIES_RESOURCE_LOCATION值為什麼?
前面成員位置定義好了
再回頭看看loadFactoryNames方法的下面有一些話
如下位置 還有一些話,是一條斷言
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."
意思是:configurations必須非空,否則就列印一段話,No auto configuration classes found in META-INF/spring.factories
所以我們有必要去找找這個檔案看看
(這個檔案記好了,整個J2EE的自動配置元件都在springboot-autoconfigure的jar包中)
開啟之後會發現裡面包含了很多自動配置屬性
比如點開webmvc的
然後會看到很多關於mvc的配置
那麼掃描到的這些檔案作用是什麼呢:是把這個檔案的urls拿到之後並把這些urls每一個遍歷,最終把這些檔案整成一個properties物件,繼續看loadSpringFactories方法
然後從properties物件裡邊獲取一些值,把這些獲取到的值來載入我們最終要返回的這個結果
這個結果result為map集合,然後返回到loadFactoryNames方法中
這個factoryTypeName值為EnableAutoConfiguration
因為loadFactoryNames方法攜帶過來的第一個引數為EnableAutoConfiguration.class,所以factoryType值也為EnableAutoConfiguration.class,那麼factoryTypeName值為EnableAutoConfiguration
那麼map集合中getOrDefault方法為什麼意思呢?意思就是當Map集合中有這個key時,就使用這個key值,如果沒有就使用預設值defaultValue(第二個引數),所以是判斷是否包含EnableAutoConfiguration
看下圖,這不是包含著嘛
所以不得而知了,意為把spring-boot-autoconfigure-2.3.2.RELEASE.jar!/META-INF/spring.factories
這個檔案下,EnableAutoConfiguration下面所有的元件,每一個xxxAutoConfiguration類都是容器中的一個組
件,都加入到容器中。
加入到容器中之後的作用就是用它們來做自動配置,這就是Springboot自動配置之源,也就是自動配置的開始,
只有這些自動配置類進入到容器中以後,接下來這個自動配置類才開始進行啟動
那spring.factories中存在那麼多的配置,每次啟動時都是把它們全部載入嗎?神經病吧,不現實的
我們隨便點開一個類
再點開一個
是不是都有這個@ConditionalOnXXX註解,每一個類都有
@Conditional是spring底層註解,意思就是根據不同的條件,來進行自己不同的條件判斷,如果滿足指定的條件,那麼整個配置類裡邊的配置才會生效。
所以又不得而知了,在載入自動配置類的時候,並不是將spring.factories的配置全量載入進來,而是通過這個註解的判斷,如果註解中的類都存在,才會進行載入。
這樣就實現了springboot的自動配置
小結一下:
從網上貼的流程:
SpringBoot在啟動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值
將這些值作為自動配置類匯入容器 , 自動配置類就生效 , 幫我們進行自動配置工作;
以前我們需要自己配置的東西 , 自動配置類都幫我們解決了
整個J2EE的整體解決方案和自動配置都在springboot-autoconfigure的jar包中;
它將所有需要匯入的元件以全類名的方式返回 , 這些元件就會被新增到容器中 ;
它會給容器中匯入非常多的自動配置類 (xxxAutoConfiguration), 就是給容器中匯入這個場景需要的所有元件 , 並配置好這些元件 ;
有了自動配置類 , 免去了我們手動編寫配置注入功能元件等的工作;
本人剛接觸springboot,草草的做了這麼一個自動配置原理分析。有些表述不清楚或錯誤的地方請見諒。
最後說一句,springboot流弊!!!