淺談SpringBoot自動配置

要點:

  • 約定大於配置

  • starter、spring.factories

  • @EnableAutoConfiguration

 

前述:

MAC中的配置:

Boot中的配置:

這是springboot相較springmvc,帶給我們最直觀的使用感受

曾經在mvc框架下,我們要構建項目,需要以xml形式寫一堆配置文件,書寫易錯,配置繁雜,管理麻煩

終於!springboot的出現,將我們從這配置的苦海中帶到天堂。

"腰不疼了,頭髮掉的也少了"

 

詳述:

一、"約定大於配置"

百度的意思是這樣的:
“約定優於配置(convention over configuration),也稱作按約定編程,是一種軟件設計範式,旨在減少軟件開發人員需做決定的數量,獲得簡單的好處,而又不失靈活性。”

這個是springboot一切的基礎,我們構建一個項目基本配置大都相同,而springboot則已經爲我們提供了默認配置。
我們則基於這些默認的配置進行項目構建,如有不同的配置,則可另行在application.ym中進行配置即可

springboot中的約定:

  • Maven的目錄結構。默認有resources文件夾,存放資源配置文件。src-main-resources,src-main-java。默認的編譯生成的類都在targe文件夾下面
  • spring boot默認的配置文件必須是,也只能是application.命名的yml文件或者properties文件,且唯一
  • application.yml中默認屬性。數據庫連接信息必須是以spring: datasource: 爲前綴;多環境配置。該屬性可以根據運行環境自動讀取不同的配置文件;端口號、請求路徑等

總而言之:基於此約定的springboot,爲我們簡化了spring配置!爽!

 

二、1.Starter

“spring-boot-starter-web”,是我們使用springboot構建項目時,在pom中最常引用的Starter
spring-boot-starter-*起步依賴是SpringBoot核心之處,它提供了Spring和相關技術提供"一條龍"依賴與服務,讓開發者不必關心Spring相關配置,簡化了傳統的xml等配置操作,當然了開發者也可通過application.ym/properties文件自定義配置

這不是與上文前後呼應?所以,可以說 starter-* 是springboot爲我們提供"一條龍"服務,最直觀的體現!爽!

那什麼是Starter?
簡單說:就是一個場景/環境所需的一組jar依賴!
避免了我們每個項目構建一個環境/服務時,重複引入依賴並配置,繁瑣且低效。那starter就把一個環境/服務所需的依賴組合起來爲一個starter,需要時直接引starter就好了
其實和Docker理念比較類似,都是在做一個"包裝操作"

以spring-boot-starter-web爲例:

點進去康一下:

我們看到只有一個pom文件,在其中,有兩部分:

  1. 引入了我們構建web項目所必須的依賴
  2. 引入自動配置的jar,pring-boot-starter會引入spring-boot-autoconfigure的依賴。
    雖然不同的starter實現起來各有差異,但是他們基本上都會使用到兩個相同的內容:ConfigurationProperties和AutoConfiguration。因爲Spring Boot堅信“約定大於配置”這一理念,所以我們使用ConfigurationProperties來保存我們的配置,並且這些配置都可以有一個默認值,即在我們沒有主動覆寫原始配置的情況下,默認值就會生效,這在很多情況下是非常有用的。除此之外,starter的ConfigurationProperties還使得所有的配置屬性被聚集到一個文件中(一般在resources目錄下的application.properties),這樣我們就告別了Spring項目中XML地獄。

2.spring.factories

自動配置類的jar的是有一個META-INF/spring.factories文件內容如下:

\是爲了換行也可以使用,可以看到配置的結構形式是Key=>Value形式,多個Value時使用,隔開。

應用在啓動時就會加載spring-boot-autoconfigure的jar包下面的META-INF/spring.factories文件中定義的autoconfiguration類。將configuration類中定義的bean加入spring到容器中。就相當於加載之前我們自己配置組件的xml文件。而現在SpringBoot自己定義了一個默認的值,然後直接加載進入了Spring容器。

這樣我們引入的starter就可以直接被springboot啓動時初始化配置,並被Spring容器管理,爲我們提供“服務”~

那麼....問題來了,我們的springboot如何進行"自動配置"的呢?
 

三、@EnableAutoConfiguration自動配置

(1.)@SpringBootApplication

(2.)點進去瞅瞅

  1. @SpringBootConfiguration:註解中引入了@Configuration,表明SpringBootApplication是一個配置類
  2. @EnableAutoConfiguration:開啓自動配置功能。
  3. @ComponentScan:自動掃描

(3.)進入@EnableAutoConfiguration

  1. @AutoConfigurationPackage:當SpringBoot應用啓動時默認會將啓動類所在的package作爲自動配置的package
  2. @Import(AutoConfigurationImportSelector.class):利用@Import加載自動配置類,進行自動配置

(4.)進入AutoConfigurationImportSelector.class

我們可以從下面方式進入相應源碼中

getAutoConfigurationEntry -> getCandidateConfigurations ->SpringFactoriesLoader.loadFactoryNames() ->loadSpringFactories()

我們會看到相應的獲取自動配只的源碼:

// 使用類加載器找META-INF/spring.factories資源
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();
                // 遍歷找到的資源
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    // 使用屬性文件加載資源
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

通過這種方式,會獲取到META-INF/spring.factories文件,並通過其全限定名,之後可通過反射機制將該配置類加入到Spring容器中進行管理。

工廠加載機制:這裏採用的是工廠加載機制,由SpringFactoriesLoader完成,SpringFactoriesLoaderl使用loadFactoryNames方法,從META-INF/spring.factories文件路徑下加載指定接口的實現類

 

但是!AutoConfigurationImportSelector如何被加載?何時被加載?

實現了DeferredImportSelector接口,該又繼承了ImportSelector

DeferredImportSelector是在所有@Configuration處理完成後才被處理的

當我們啓動springboot啓動類後,會加載實現了DeferredImportSelector接口的process()方法,在process()中會調用getAutoConfigurationEntry(),然後就是我們上面的調用鏈路了~

總述:

到這裏,springboot的自動配置就梳理完成了~

總的來說:就是利用Spring的工廠加載機制,加載我們約定好的META-INF/spring.factories文件中的接口的實現類

"約定大於配置~"

如有不對或疑惑點歡迎留言交流~

 

參考文檔:
https://baijiahao.baidu.com/s?id=1637222519335395430&wfr=spider&for=pc
https://blog.csdn.net/jdfk423/article/details/82940924
https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/
https://www.jianshu.com/p/29f3a2f65282
https://blog.csdn.net/WiteWater/article/details/90056165
https://www.jianshu.com/p/23d4e853b15b
https://blog.csdn.net/weixin_41532316/article/details/102509707
https://www.cnblogs.com/huojg-21442/articles/12335457.html
https://www.jianshu.com/p/aa99a303bc37
https://www.jianshu.com/p/23d4e853b15b
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章