注:該源碼分析對應SpringBoot版本爲2.1.0.RELEASEhtml
本篇接 外部配置屬性值是如何被綁定到XxxProperties類屬性上的?--SpringBoot源碼(五)java
溫故而知新,咱們來簡單回顧一下上篇的內容,上一篇咱們分析了SpringBoot外部配置屬性值是如何被綁定到XxxProperties類屬性上的相關源碼,現將外部屬性綁定的重要步驟總結以下:git
@EnableConfigurationProperties
註解import
了EnableConfigurationPropertiesImportSelector
後置處理器;EnableConfigurationPropertiesImportSelector
後置處理器又向Spring
容器中註冊了ConfigurationPropertiesBeanRegistrar
和ConfigurationPropertiesBindingPostProcessorRegistrar
這兩個bean
;ConfigurationPropertiesBeanRegistrar
向Spring
容器中註冊了XxxProperties
類型的bean
;ConfigurationPropertiesBindingPostProcessorRegistrar
向Spring
容器中註冊了ConfigurationBeanFactoryMetadata
和ConfigurationPropertiesBindingPostProcessor
兩個後置處理器;ConfigurationBeanFactoryMetadata
後置處理器在初始化bean
factory
時將@Bean
註解的元數據存儲起來,以便在後續的外部配置屬性綁定的相關邏輯中使用;ConfigurationPropertiesBindingPostProcessor
後置處理器將外部配置屬性值綁定到XxxProperties
類屬性的邏輯委託給ConfigurationPropertiesBinder
對象,而後ConfigurationPropertiesBinder
對象又最終將屬性綁定的邏輯委託給Binder
對象來完成。可見,重要的是上面的第5步。github
咱們都知道,SpringBoot內置了各類Starter
起步依賴,咱們使用很是方便,大大減輕了咱們的開發工做。有了Starter
起步依賴,咱們不用去考慮這個項目須要什麼庫,這個庫的groupId
和artifactId
是什麼?更不用擔憂引入這個版本的庫後會不會跟其餘依賴有沒有衝突。web
舉個栗子:如今咱們想開發一個web項目,那麼只要引入
spring-boot-starter-web
這個起步依賴就能夠了,不用考慮要引入哪些版本的哪些依賴了。像之前咱們還要考慮引入哪些依賴庫,好比要引入spring-web
和spring-webmvc
依賴等;此外,還要考慮引入這些庫的哪些版本纔不會跟其餘庫衝突等問題。spring
那麼咱們今天暫時不分析SpringBoot自動配置的源碼,因爲起步依賴跟自動配置的關係是如影隨形的關係,所以本篇先站在maven項目構建的角度來宏觀分析下咱們平時使用的SpringBoot內置的各類Starter
是怎樣構建的?apache
在分析SpringBoot內置的各類Starter
構建原理前,咱們先來認識下Maven的optional
標籤,由於這個標籤起到相當重要的做用。
Maven的optional
標籤表示可選依賴即不可傳遞的意思,下面直接舉個栗子來講明。tomcat
好比有A
,B
和C
三個庫,C
依賴B
,B
依賴A
。下面看下這三個庫的pom.xml
文件:springboot
// A的pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <groupId>com.ymbj</groupId> <artifactId>A</artifactId> <version>1.0-SNAPSHOT</version> </project>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <groupId>com.ymbj</groupId> <artifactId>B</artifactId> <version>1.0-SNAPSHOT</version> <!--注意是可選依賴--> <dependencies> <dependency> <groupId>com.ymbj</groupId> <artifactId>A</artifactId> <version>1.0-SNAPSHOT</version> <optional>true</optional> </dependency> </dependencies> </project>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <groupId>com.ymbj</groupId> <artifactId>C</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.ymbj</groupId> <artifactId>B</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
上面三個A
,B
和C
庫的pom.xml
可知,B
庫依賴A
庫,而後C
庫又依賴了B
庫,那麼請想一下,Maven打包構建C
庫後,A
庫有沒有被引進來?mybatis
答案確定是沒有,由於B
庫引入A
庫依賴時使用了<optional>true</optional>
,即將Maven的optional
標籤值設爲了true
,此時C
庫再引入B
庫依賴時,A
庫是不會被引入到C
庫的。
同時跟Maven傳遞依賴有關的還有一個exclusions
標籤,這個表示將某個庫的某個子依賴排除掉,這裏再也不詳述。
咱們如今來探究SpringBoot內置的各類Starter
究竟是怎樣構建的呢?
還記得如何分析SpringBoot源碼模塊及結構?這篇文章分析的SpringBoot內部的模塊之間的關係嗎?先來回顧一下SpringBoot源碼內部模塊圖:
<center>圖1</center>
咱們都知道,SpringBoot的Starter
的構建的原理實質就是自動配置,所以由圖1能夠看到SpringBoot源碼項目內部跟Starter
及其自動配置有關的模塊有四個:spring-boot-starters
,spring-boot-actuator-autoconfigure
,spring-boot-autoconfigure
和spring-boot-test-autoconfigure
。 每一個模塊的做用請看如何分析SpringBoot源碼模塊及結構?這篇文章,這裏再也不贅述。
那麼,spring-boot-starters
模塊跟後面三個自動配置有關的模塊xxx-autoconfigure
模塊的關係是怎樣的呢?
此時咱們先來看看spring-boot-starters
模塊裏面的結構是怎樣的?
<center>圖2</center>
由圖2能夠看到spring-boot-starters
模塊包含了SpringBoot內置的各類starter
:spring-boot-starter-xxx
。因爲SpringBoot內置的各類starter
太多,以咱們經常使用的spring-boot-starter-web
起步依賴來探究好了。
咱們首先看下spring-boot-starter-web
模塊內部結構:
<center>圖3</center>
能夠看到spring-boot-starter-web
模塊裏面只有.flattened-pom.xml
和pom.xml
文件,而沒有任何代碼!有點出乎咱們意料。咱們都知道若要用到SpringBoot的web功能時引入spring-boot-starter-web
起步依賴便可,而如今spring-boot-starter-web
模塊裏面沒有一行代碼,那麼spring-boot-starter-web
到底是如何構建的呢?會不會跟圖1所示的spring-boot-autoconfigure
自動配置模塊有關?
此時咱們就須要看下spring-boot-starter-web
模塊的pom.xml
文件內容:
<center>圖4</center>
由圖4能夠看到,spring-boot-starter-web
模塊依賴了spring-boot-starter
,spring-boot-starter-tomcat
,spring-web
和spring-webmvc
等模塊,竟然沒有依賴spring-boot-autoconfigure
自動配置模塊!
因爲spring-boot-starter-web
模塊確定跟spring-boot-autoconfigure
自動配置模塊有關,因此spring-boot-starter-web
模塊確定是間接依賴了spring-boot-autoconfigure
自動配置模塊。
圖4標有標註"重點關注"的spring-boot-starter
模塊是絕大部分spring-boot-starter-xxx
模塊依賴的基礎模塊,是核心的Starter
,包括了自動配置,日誌和YAML
支持。咱們此時來關注下spring-boot-starter
的pom.xml
文件,也許其依賴了了spring-boot-autoconfigure
自動配置模塊。
<center>圖5</center>
由圖5能夠看到,咱們前面的猜測沒有錯,正是spring-boot-starter
模塊依賴了spring-boot-autoconfigure
自動配置模塊!所以,到了這裏咱們就能夠得出結論了:spring-boot-starter-web
模塊沒有一行代碼,可是其經過spring-boot-starter
模塊間接依賴了spring-boot-autoconfigure
自動配置模塊,從而實現了其起步依賴的功能。
此時咱們再來看下spring-boot-autoconfigure
自動配置模塊的內部包結構:
<center>圖6</center>
由圖6紅框處,咱們能夠知道spring-boot-starter-web
起步依賴的自動配置功能原來是由spring-boot-autoconfigure
模塊的web
包下的類實現的。
到了這裏spring-boot-starter-web
起步依賴的構建基本原理咱們就搞清楚了,可是還有一個特別重要的關鍵點咱們還沒Get到。這個關鍵點跟Maven的optional
標籤有的做用有關。
爲了Get到這個點,咱們先來思考一個問題:平時咱們開發web
項目爲何引入了spring-boot-starter-web
這個起步依賴後,spring-boot-autoconfigure
模塊的web
相關的自動配置類就會起自動起做用呢?
咱們應該知道,某個自動配置類起做用每每是因爲classpath
中存在某個類,這裏以DispatcherServletAutoConfiguration
這個自動配置類爲切入點去Get這個點好了。
先看下DispatcherServletAutoConfiguration
可以自動配置的條件是啥?
<center>圖7</center>
由圖7所示,DispatcherServletAutoConfiguration
可以自動配置的條件之一是@ConditionalOnClass(DispatcherServlet.class)
,即只有classpath
中存在DispatcherServlet.class
這個類,那麼DispatcherServletAutoConfiguration
自動配置相關邏輯才能起做用。
而DispatcherServlet
這個類是在spring-webmvc
這個依賴庫中的,以下圖所示:
<center>圖8</center>
此時咱們再看下spring-boot-autoconfigure
模塊的pom.xml
文件引入spring-webmvc
這個依賴的狀況:
<center>圖9</center>
由圖9所示,spring-boot-autoconfigure
模塊引入的spring-webmvc
這個依賴時optional
被設置爲true
,原來是可選依賴。即spring-webmvc
這個依賴庫只會被導入到spring-boot-autoconfigure
模塊中,而不會被導入到間接依賴spring-boot-autoconfigure
模塊的spring-boot-starter-web
這個起步依賴中。
此時,咱們再來看看spring-boot-starter-web
的pom.xml
文件的依賴狀況:
<center>圖10</center>
由圖10所示,spring-boot-starter-web
起步依賴顯式引入了spring-webmvc
這個依賴庫,即引入spring-webmvc
時沒有optional
這個標籤,又由於DispatcherServlet
這個類是在spring-webmvc
這個依賴庫中的,從而classpath
中存在DispatcherServlet
這個類,所以DispatcherServletAutoConfiguration
這個自動配置類就生效了。固然,web
相關的其餘自動配置類生效也是這個原理。
至此,咱們也明白了spring-boot-autoconfigure
模塊爲何要把引入的spring-webmvc
這個依賴做爲可選依賴了,其目的就是爲了在spring-boot-starter-web
起步依賴中能顯式引入spring-webmvc
這個依賴(這個起決定性做用),從而咱們開發web項目只要引入了spring-boot-starter-web
起步依賴,那麼web相關的自動配置類就生效,從而能夠開箱即用這個就是spring-boot-starter-web
這個起步依賴的構建原理了。
前面提到的spring-boot-starter-actuator
,spring-boot-starter-test
及其餘內置的spring-boot-starter-xxx
的起步依賴的構建原理也是如此,只不過spring-boot-starter-actuator
依賴的是spring-boot-actuator-autoconfigure
,spring-boot-starter-test
依賴的是spring-boot-test-autoconfigure
模塊罷了,這裏再也不詳述。
思考:
spring-boot-actuator-autoconfigure
的pom.xml
文件引入了20多個可選依賴,而爲何spring-boot-starter-actuator
起步依賴只引入了micrometer-core
這個依賴呢?
前面分析了SpringBoot內置的各類Starter
的構建原理,理論聯繫實踐,那麼若是可以動手實踐一下自定義Starter
那就更好了。
下面提供一個自定義Starter
的一個簡單Demo
,這個Demo
徹底模仿SpringBoot
內置Starter
的內部包結構來編寫,對於進一步瞭解SpringBoot內置的各類Starter
的構建原理頗有幫助。
下面是這個Demo
的github地址,推薦給有興趣的小夥伴們。
模仿springboot內部結構自定義Starter。此外,如何自定義一個Starter
,能夠參考下Mybatis的spring-boot-starter是如何編寫的。
好了,SpringBoot內置的各類Starter
的構建原理分析就到此結束了,現將關鍵點總結下:
spring-boot-starter-xxx
起步依賴沒有一行代碼,而是直接或間接依賴了xxx-autoconfigure
模塊,而xxx-autoconfigure
模塊承擔了spring-boot-starter-xxx
起步依賴自動配置的實現;xxx-autoconfigure
自動配置模塊引入了一些可選依賴,這些可選依賴不會被傳遞到spring-boot-starter-xxx
起步依賴中,這是起步依賴構建的關鍵點;spring-boot-starter-xxx
起步依賴顯式引入了一些對自動配置起做用的可選依賴;bean
等。原創不易,幫忙點個讚唄!
因爲筆者水平有限,若文中有錯誤還請指出,謝謝。
參考:
1,Maven 依賴傳遞性透徹理解
歡迎關注【源碼筆記】公衆號,一塊兒學習交流。