當前位置:首頁 » 存儲配置 » spring如何完成自動配置

spring如何完成自動配置

發布時間: 2023-02-14 00:23:54

⑴ SpringBoot核心原理:自動配置、事件驅動、Condition

SpringBoot是Spring的包裝,通過自動配置使得SpringBoot可以做到開箱即用,上手成本非常低,但是學習其實現原理的成本大大增加,需要先了解熟悉Spring原理。

如果還不清楚Spring原理的,可以先查看博主之前的文章,本篇主要分析SpringBoot的啟動、自動配置、Condition、事件驅動原理。

SpringBoot啟動非常簡單,因其內置了Tomcat,所以只需要通過下面幾種方式啟動即可:

可以看到第一種是最簡單的,也是最常用的方式,需要注意類上面需要標注 @SpringBootApplication 註解,這是自動配置的核心實現,稍後分析,先來看看SpringBoot啟動做了些什麼?

在往下之前,不妨先猜測一下,run方法中需要做什麼?對比Spring源碼,我們知道,Spring的啟動都會創建一個 ApplicationContext 的應用上下文對象,並調用其refresh方法啟動容器,SpringBoot只是Spring的一層殼,肯定也避免不了這樣的操作。

另一方面,以前通過Spring搭建的項目,都需要打成War包發布到Tomcat才行,而現在SpringBoot已經內置了Tomcat,只需要打成Jar包啟動即可,所以在run方法中肯定也會創建對應的Tomcat對象並啟動。以上只是我們的猜想,下面就來驗證,進入run方法:

SpringBoot的啟動流程就是這個方法,先看 getRunListeners 方法,這個方法就是去拿到所有的 SpringApplicationRunListener 實現類,這些類是用於SpringBoot事件發布的,關於事件驅動稍後分析,這里主要看這個方法的實現原理:

一步步追蹤下去可以看到最終就是通過SPI機制根據介面類型從 META-INF/spring.factories 文件中載入對應的實現類並實例化,SpringBoot的自動配置也是這樣實現的。

為什麼要這樣做呢?通過註解掃描不可以么?當然不行,這些類都在第三方jar包中,註解掃描實現是很麻煩的,當然你也可以通過 @Import 註解導入,但是這種方式不適合擴展類特別多的情況,所以這里採用SPI的優點就顯而易見了。

回到run方法中,可以看到調用了 createApplicationContext 方法,見名知意,這個就是去創建應用上下文對象:

注意這里通過反射實例化了一個新的沒見過的上下文對象 ,這個是SpringBoot擴展的,看看其構造方法:

如果你有看過Spring註解驅動的實現原理,這兩個對象肯定不會陌生,一個實支持註解解析的,另外一個是掃描包用的。

上下文創建好了,下一步自然就是調用refresh方法啟動容器:

這里首先會調用到其父類中 :

可以看到是直接委託給了父類:

這個方法不會陌生吧,之前已經分析過了,這里不再贅述,至此SpringBoot的容器就啟動了,但是Tomcat啟動是在哪裡呢?run方法中也沒有看到。

實際上Tomcat的啟動也是在refresh流程中,這個方法其中一步是調用了onRefresh方法,在Spring中這是一個沒有實現的模板方法,而SpringBoot就通過這個方法完成了Tomcat的啟動:

這里首先拿到 TomcatServletWebServerFactory 對象,通過該對象再去創建和啟動Tomcat:

上面的每一步都可以對比Tomcat的配置文件,需要注意默認只支持了http協議:

如果想要擴展的話則可以對 additionalTomcatConnectors 屬性設置值,需要注意這個屬性沒有對應的setter方法,只有 addAdditionalTomcatConnectors 方法,也就是說我們只能通過實現 BeanFactoryPostProcessor 介面的 postProcessBeanFactory 方法,而不能通過 的 方法,因為前者可以通過傳入的BeanFactory對象提前獲取到 TomcatServletWebServerFactory 對象調用 addAdditionalTomcatConnectors 即可;而後者只能拿到BeanDefinition對象,該對象只能通過setter方法設置值。

這段代碼會在控制台列印所有的事件名稱,按照順序如下:

以上是正常啟動關閉,如果發生異常還有發布 ApplicationFailedEvent 事件。事件的發布遍布在整個容器的啟動關閉周期中,事件發布對象剛剛我們也看到了是通過SPI載入的 SpringApplicationRunListener 實現類 EventPublishingRunListener ,同樣事件監聽器也是在 spring.factories 文件中配置的,默認實現了以下監聽器:

可以看到有用於文件編碼的( ),有載入日誌框架的( LoggingApplicationListener ),還有載入配置的( ConfigFileApplicationListener )等等一系列監聽器,SpringBoot也就是通過這系列監聽器將必要的配置和組件載入到容器中來,這里不再詳細分析,感興趣的讀者可以通過其實現的 onApplicationEvent 方法看到每個監聽器究竟是監聽的哪一個事件,當然事件發布和監聽我們自己也是可以擴展的。

SpringBoot最核心的還是自動配置,為什麼它能做到開箱即用,不再需要我們手動使用 @EnableXXX 等註解來開啟?這一切的答案就在 @SpringBootApplication 註解中:

這里重要的註解有三個: @SpringBootConfiguration 、 @EnableAutoConfiguration 、 @ComponentScan 。 @ComponentScan 就不用再說了, @SpringBootConfiguration 等同於 @Configuration ,而 @EnableAutoConfiguration 就是開啟自動配置:

@AutoConfigurationPackage 註解的作用就是將該註解所標記類所在的包作為自動配置的包,簡單看看就行,主要看 ,這個就是實現自動配置的核心類,注意這個類是實現的 DeferredImportSelector 介面。

在這個類中有一個 selectImports 方法。這個方法在我之前的文章這一次搞懂Spring事務註解的解析也有分析過,只是實現類不同,它同樣會被 類調用,先來看這個方法做了些什麼:

追蹤源碼最終可以看到也是從 META-INF/spring.factories 文件中拿到所有 EnableAutoConfiguration 對應的值(在 spring-boot-autoconfigure 中)並通過反射實例化,過濾後包裝成 AutoConfigurationEntry 對象返回。

看到這里你應該會覺得自動配置的實現就是通過這個 selectImports 方法,但實際上這個方法通常並不會被調用到,而是會調用該類的內部類 AutoConfigurationGroup 的process和selectImports方法,前者同樣是通過 getAutoConfigurationEntry 拿到所有的自動配置類,而後者這是過濾排序並包裝後返回。

下面就來分析 是怎麼調用到這里的,直接進入 processConfigBeanDefinitions 方法:

前面一大段主要是拿到合格的 Configuration 配置類,主要邏輯是在 ConfigurationClassParser.parse 方法中,該方法完成了對 @Component 、 @Bean 、 @Import 、 @ComponentScans 等註解的解析,這里主要看對 @Import 的解析,其它的讀者可自行分析。一步步追蹤,最終會進入到 processConfigurationClass 方法:

這里需要注意 this.conditionEvaluator.shouldSkip 方法的調用,這個方法就是進行Bean載入過濾的,即根據 @Condition 註解的匹配值判斷是否載入該Bean,具體實現稍後分析,繼續跟蹤主流程 doProcessConfigurationClass :

這里就是完成對一系列註解的支撐,我省略掉了,主要看 processImports 方法,這個方法就是處理 @Import 註解的:

剛剛我提醒過 是實現 DeferredImportSelector 介面的,如果不是該介面的實現類則是直接調用 selectImports 方法,反之則是調用 DeferredImportSelectorHandler.handle 方法:

首先創建了一個 DeferredImportSelectorHolder 對象,如果是第一次執行則是添加到 deferredImportSelectors 屬性中,等到 ConfigurationClassParser.parse 的最後調用process方法:

反之則是直接執行,首先通過register拿到 AutoConfigurationGroup 對象:

然後在 processGroupImports 方法中進行真正的處理:

在 getImports 方法中就完成了對process和 selectImports 方法的調用,拿到自動配置類後再遞歸調用調用 processImports 方法完成對自動配置類的載入。至此,自動配置的載入過程就分析完了,下面是時序圖:

在自動配置類中有很多Condition相關的註解,以AOP為例:

這里就能看到 @ConditionalOnProperty 、 @ConditionalOnClass 、 @ConditionalOnMissingClass ,另外還有 @ConditionalOnBean 、 @ConditionalOnMissingBean 等等很多條件匹配註解。

這些註解表示條件匹配才會載入該Bean,以 @ConditionalOnProperty 為例,表明配置文件中符合條件才會載入對應的Bean,prefix表示在配置文件中的前綴,name表示配置的名稱, havingValue 表示配置為該值時才匹配, matchIfMissing 則是表示沒有該配置是否默認載入對應的Bean。其它註解可類比理解記憶,下面主要來分析該註解的實現原理。

這里註解點進去看會發現每個註解上都標注了 @Conditional 註解,並且value值都對應一個類,比如 OnBeanCondition ,而這些類都實現了 Condition 介面,看看其繼承體系:

上面只展示了幾個實現類,但實際上Condition的實現類是非常多的,我們還可以自己實現該介面來擴展 @Condition 註解。Condition介面中有一個matches方法,這個方法返回true則表示匹配。該方法在 ConfigurationClassParser 中多處都有調用,也就是剛剛我提醒過的shouldSkip方法,具體實現是在 ConditionEvaluator 類中:

再來看看matches的實現,但 OnBeanCondition 類中沒有實現該方法,而是在其父類 SpringBootCondition 中:

getMatchOutcome 方法也是一個模板方法,具體的匹配邏輯就在這個方法中實現,該方法返回的 ConditionOutcome 對象就包含了是否匹配和日誌消息兩個欄位。進入到 OnBeanCondition 類中:

可以看到該類支持了 @ConditionalOnBean 、 @ConditionalOnSingleCandidate 、 @ConditionalOnMissingBean 註解,主要的匹配邏輯在 getMatchingBeans 方法中:

這里邏輯看起來比較復雜,但實際上就做了兩件事,首先通過 getNamesOfBeansIgnoredByType 方法調用 beanFactory.getBeanNamesForType 拿到容器中對應的Bean實例,然後根據返回的結果判斷哪些Bean存在,哪些Bean不存在(Condition註解中是可以配置多個值的)並返回MatchResult對象,而MatchResult中只要有一個Bean沒有匹配上就返回false,也就決定了當前Bean是否需要實例化。

本篇分析了SpringBoot核心原理的實現,通過本篇相信讀者也將能更加熟練地使用和擴展SpringBoot。

另外還有一些常用的組件我沒有展開分析,如事務、MVC、監聽器的自動配置,這些我們有了Spring源碼基礎的話下來看一下就明白了,這里就不贅述了。

最後讀者可以思考一下我們應該如何自定義starter啟動器,相信看完本篇應該難不倒你。

⑵ 如何使用Spring Boot的自動配置

第一步,編寫配置Bean——PrintAfterInitBean
代碼如下,因為只是一個簡單例子,這里的配置Bean其實可以是其他任何復雜配置Bean,例如DataSource。往往一個公共包需要多個這樣配置Bean才能完成其配置。
public class PrintAfterInitBean implements InitializingBean {
private String message;

public void afterPropertiesSet() throws Exception {
System.out.println(message);
}
//setter getter
}

第二步,創建一個AutoConfiguration。
如果搜索Spring Boot下面的類,你會發現其實有很多名字形如xxxAutoConfiguration的類,這些類都是Spirng Boot為我們做的一些快捷配置類。 創建一個TestAutoConfig,作為一個自動配置類
@Configuration
public class TestAutoConfig {

@Bean
@ConfigurationProperties(prefix = "init")
@ConditionalOnMissingBean(PrintAfterInitBean.class)
@ConditionalOnProperty(prefix = "init",value = "message")
public PrintAfterInitBean printAfterInitBean() {
return new PrintAfterInitBean();
}
}

@ConfigurationProperties 是Spring Boot提供的方便屬性注入的註解,功能其實和@Value類似
@ConditionalOnMissingBean 表示當BeanFactory中沒有PrintAfterInitBean類型的Bean才會創建,否則就會忽略這個Bean。這個就是上圖中所謂的【滿足自動配置條件】,同理的,ConditionalOnProperty表示當存在配置前綴為init,配置值為message的配置的時候,才會生效。@ConditionalOnXXX 系列的註解都是為了在自動配置中,不侵入用戶的配置。
第三步,創建spring.factories
在resources下面創建META-INF/spring.factories, 然後在文件中把第二步的類配置進去
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.netease.xxx.xxx.UrsPropertyAutoConfig

這樣就完成一個Spring Boot自動配置,如果存在init.message的配置,那麼spring boot啟動的時候就會列印init.message配置對應值。

⑶ Spring自動裝配原理

其中它主要是依賴一個父項目,主要是管理項目的資源過濾及插件!

點進去,發現還有一個父依賴

這里才是真正管理SpringBoot應用裡面所有依賴版本的地方,SpringBoot的版本控制中心;
以後我們導入依賴默認是不需要寫版本;但是如果導入的包沒有在依賴中管理著就需要手動配置版本了;

springboot-boot-starter-xxx :就是spring-boot的場景啟動器
SpringBoot將所有的功能場景都抽取出來,做成一個個的starter (啟動器),只需要在項目中引入這些starter即可,所有相關的依賴都會導入進來 , 我們要用什麼功能就導入什麼樣的場景啟動器即可 ;我們未來也可以自己自定義 starter;

默認的主啟動類

但是一個簡單的啟動類並不簡單!我們來分析一下這些註解都幹了什麼
@SpringBootApplication
作用:標注在某個類上說明這個類是SpringBoot的主配置類 , SpringBoot就應該運行這個類的main方法來啟動SpringBoot應用;

進入這個註解:可以看到上面還有很多其他註解!

@ComponentScan
這個註解在Spring中很重要 ,它對應XML配置中的元素。
作用:自動掃描並載入符合條件的組件或者bean , 將這個bean定義載入到IOC容器中
@SpringBootConfiguration
作用:SpringBoot的配置類 ,標注在某個類上 , 表示這是一個SpringBoot的配置類;

我們繼續進去這個註解查看

這里的 @Configuration,說明這是一個配置類 ,配置類就是對應Spring的xml 配置文件;
裡面的 @Component 這就說明,啟動類本身也是Spring中的一個組件而已,負責啟動應用!
我們回到 SpringBootApplication 註解中繼續看。

@EnableAutoConfiguration :開啟自動配置功能
以前我們需要自己配置的東西,而現在SpringBoot可以自動幫我們配置 ;@EnableAutoConfiguration告訴SpringBoot開啟自動配置功能,這樣自動配置才能生效;

點進註解接續查看:
@AutoConfigurationPackage :自動配置包

@import :Spring底層註解@import , 給容器中導入一個組件
Registrar.class 作用:將主啟動類所在包及包下面所有子包裡面的所有組件掃描到Spring容器 ;
這個分析完了,退到上一步,繼續看

@Import({.class}) :給容器導入組件 ;

:自動配置導入選擇器,那麼它會導入哪些組件的選擇器呢?我們點擊去這個類看源碼:

1、這個類中有一個這樣的方法:

2、這個方法又調用了 SpringFactoriesLoader 類的靜態方法!我們進入SpringFactoriesLoader類loadFactoryNames() 方法

3、我們繼續點擊查看 loadSpringFactories 方法

4、發現一個多次出現的文件:spring.factories,全局搜索它

spring.factories
我們根據源頭打開spring.factories , 看到了很多自動配置的文件;這就是自動配置根源所在!

可以看到這些一個個的都是javaConfig配置類,而且都注入了一些Bean,可以找一些自己認識的類,看著熟悉一下!

所以,自動配置真正實現是從classpath中搜尋所有的META-INF/spring.factories配置文件 ,並將其中對應的 org.springframework.boot.autoconfigure. 包下的配置項,通過反射實例化為對應標注了 @Configuration的JavaConfig形式的IOC容器配置類 , 然後將這些都匯總成為一個實例並載入到IOC容器中。

結論:

不簡單的方法
我最初以為就是運行了一個main方法,沒想到卻開啟了一個服務;

SpringApplication.run分析
分析該方法主要分兩部分,一部分是SpringApplication的實例化,二是run方法的執行

springApplication這個類主要做了以下四件事情:
1、推斷應用的類型是普通的項目還是Web項目
2、查找並載入所有可用初始化器 , 設置到initializers屬性中
3、找出所有的應用程序監聽器,設置到listeners屬性中
4、推斷並設置main方法的定義類,找到運行的主類

run方法流程分析

⑷ SpringBoot入門-自動配置詳解

通過查看SpringBootApplication的源碼,會發現這是一個組合註解,其中最重要的註解是@EnableAutoConfiguration

先看@AutoConfigurationPackage這個註解

裡面導入了一個Registrar類,這個類實現了bean的掃描與注冊,那它掃描的是哪個包呢?

只要看PackageImports這個類,會發現如果沒有用@ComponentScan指定包名,他默認掃描的是啟動類的包名,比如你的啟動類是cn.hollycloud.App,它掃描的就是cn.hollycloud

再來看這個類,這個類用來載入所有的自動配置項

通過上面的源碼我們知道spring把所有配置項都導進來了,但我們並不需要所有的功能。比如說我開發的時候並不需要mongodb相關功能,但spring也會把相關配置項載入進來,怎麼關閉該功能呢?看下mongodb的自動配置源碼

重點是@ConditionalOnClass(MongoClient.class),這個的意思是只有類路徑中存在MongoClient.class,也就是我們導入mongo相關依賴,這個配置項才會開啟,否則不會注冊這個bean。

同時我們看下面有個MongoClient的bean,spring很貼心地為我們初始化好了mongo的客戶端,我們直接使用就行了,如果想自定義客戶端怎麼辦呢?也很簡單,直接自己初始化一個mongo客戶端放入spring容器就行了,@ConditionalOnMissingBean的意思是如果你沒有自定義客戶端它才會自己生成一個,是不是很方便,這個叫條件化註解

現在我們來實現一個簡單的自動配置類來鞏固下。

可以想像一下我們是一家機器人公司,專門製造高端機器人,很受客戶歡迎,但是配置機器人過於復雜,這點老是被客戶詬病,你的領導想讓你提供給客戶開機即用的產品,該如何實現呢?

首先我們創建一個機器人控制終端,這是控制終端可以操控機器人說話

接下來是自動配置項,可以自動注冊配置終端

我們想要給客戶一點自由,可以讓客戶自由配置機器人的名字和顏色,而不用管機器內部復雜的操作

接下來最重要的一步是把自動配置項放到類路徑的/META-INF/spring.factories裡面

然後客戶直接引用你提供的依賴就能直接控制機器人了,而不用管復雜的初始化操作

來控制機器人說話吧,直接注入robot就能使用了,不需要客戶關心復雜的初始化操作了

如果客戶想為機器人改個名字也很簡單,直接在application.yml配置下就行了

這個例子雖然很簡單,但是說明了自動配置的工作原理,spring內置的自動配置雖然復雜,但原理都一樣的。

參考代碼: https://gitee.com/huatin/java-test 下的AutoConfigTest模塊

熱點內容
安卓微信好友刪除怎麼找回來 發布:2025-07-13 16:28:10 瀏覽:123
華為微信自動存儲 發布:2025-07-13 16:26:45 瀏覽:195
svn外網不能訪問 發布:2025-07-13 16:26:33 瀏覽:724
易語言dll加密 發布:2025-07-13 16:17:50 瀏覽:808
java編寫記事本程序 發布:2025-07-13 16:12:13 瀏覽:663
辦公室如何做數據伺服器 發布:2025-07-13 15:55:24 瀏覽:327
用一句話證明我很窮ftp 發布:2025-07-13 15:54:48 瀏覽:936
安卓如何啟動畫面 發布:2025-07-13 15:43:22 瀏覽:643
安卓哪個娃娃 發布:2025-07-13 15:35:52 瀏覽:142
伺服器列表格式錯誤什麼意思 發布:2025-07-13 15:33:05 瀏覽:170