java進階之瞭解SpringBoot的配置原理
一、Spring Boot的特點
首先我們要知道 Spring Boot 在底層已經為我們添加好瞭很多依賴。比如我們常用的Tomcat,Spring,SpringMVC這些,甚至連mysql數據庫的依賴也為我們添加好瞭
不過 SpringBoot 2.5.0 使用的mysql依賴版本是8.0.25的,如果還在使用 mysql 5 版本的小夥伴們就需要在項目的 pom.xml 文件中再次指定自己所用的依賴版本號。(因為 maven 在引入依賴時采取就近原則,你如果指定瞭依賴版本號的話,它會加載離它近的,而不會去加載遠的)
例如,我要修改 mysql 依賴的版本為5.1.43
//在當前項目裡面重寫配置 <properties> <mysql.version>5.1.43</mysql.version> </properties>
二、瞭解容器的功能
添加組件
@Configuration、@Bean註解
首先,這個註解是寫在類上面的,告訴 spring boot 這是一個配置類,等同於 以前的配置文件
配置類裡面使用@Bean
標註在方法上給容器註冊組件,默認情況下是單例的。以方法名就是組件的 id 。返回類型就是組件類型。返回的值,就是組件在容器中的實例
為什麼他會是單例的呢? 原因是在@Configuration
註解的源碼中,還定義瞭一個屬性 proxyBeanMethods ,默認值是 true。
當然我們也可以修改他的值為false,這樣他就會創建多個對象瞭。
舉個例子:
我現在在配置類裡面定義瞭一個組件,他會返回一個 User 對象,當proxyBeanMethods = true
時,無論調用多少次 user01()
方法,在容器中都隻會存在一個實例對象,但我現在把它改為 false
,來測試一下他到底是不是能創建多個實例瞭。
在主方法中進行測試:
User user=config.user01(); User user1=config.user01(); System.out.println(user==user1);
最後輸出的結果是
false
這就說明現在創建瞭兩個對象瞭,在容器中user
和user1
並不是指向同一塊內存地址
那我們什麼時候可以把它改成 false 來使用呢? 這就要設計到兩種編寫Spring Boot的方式瞭
- 一種是FULL模式 全模式(單例)
- 另一種是Lite模式 輕量級模式(非單例)
如果有組件依賴必須使用Full模式(默認)。其他默認是否Lite模式
Import註解
加入 IOC 容器的方式有很多種,上面的@Bean
是一種,這裡提到的@Import
也是用來註冊組件的,@Import
註解可以用於導入第三方包 (當然@Bean
也可以)
它是寫在類上面的,
它所創建的組件 id 默認是類的全限定名稱
具體用法參考:b站Spring註解驅動教程
Conditional註解
條件裝配:滿足Conditional指定的條件,則進行組件註入
@Conditional
註解下面還有許多的子註解
因為它的子註解實在太多瞭,下面我們具體實現一個例子來說明一下它的功能
先在 User 類中再加入一個 Pet 屬性
現在我希望容器在沒有 Pet 的情況下,我也不要 User 對象
要實現這個需求,可以這樣做,在 User 組件前面添加@ConditionalOnBean
註解,並指定條件為 Pet 組件的 id 來進行限制
執行測試方法
運行結果:
容器中是否有tomcat:false
容器中是否有user:false
這樣就對組件 User 的註冊加以限制瞭
也可以把@ConditionalOnBean(name="tom")
註解加在配置類上面,當容器中有 tom 組件時,這個類中的其他組件才會生效,否則不生效
三、原配置文件的引入
如果你原有的項目還是使用 beans.xml 等配置文件的方式來註冊組件的話,SpringBoot 是肯定無法自動配置的,那怎麼樣才能讓SpringBoot用我這個配置文件去註冊組件呢?
@ImportResource註解
使用@ImportResource
註解可以引入以前那種 xml 配置文件的方式寫的組件
使用方法:直接加在現在的配置類上面,例如:
@ImportResource("classpath:beans.xml")
配置綁定
配置綁定是什麼意思呢?其實就是使用Java讀取到properties文件中的內容,並且把它封裝到JavaBean中,以供隨時使用
具體做法:使用@ConfigurationProperties
註解
這個註解是加在你需要從 properties 屬性配置文件中要導入的屬性的類上面。
比如:我現在寫瞭一個 Car 類,然後在 properties 文件中寫好瞭它的屬性
( 註意:properties 文件中的所有屬性均要小寫,駝峰命名法也不行,可以用 – 或 _ 來代替 )
我們想要將配置文件中定義好的屬性綁定到實例對象上的話,就可以在 Car 這個類上面加上@ConfigurationProperties(prefix = "mycar")
,prefix
代表前綴的意思。
單加上這一個註解還不行,因為此時容器中還沒有這個對象,可以采用兩種方式來把 Car 這個對象加入容器中:
1.使用@Component註解
2.使用@EnableConfigurationProperties註解
@Component+@ConfigurationProperties
在@ConfigurationProperties
註解上方加上@Component
註解,即可將 Car 加入到容器中
測試方法:
運行,訪問 “/car” 查看結果
@EnableConfigurationProperties+@ConfigurationProperties
使用@EnableConfigurationProperties
註解則需要在配置類上面添加,而不是 Car 上面
這個註解的作用就是
1.開啟Car的屬性配置功能
2.把這個Car這個組件自動註冊到容器中
再次運行程序,可以得到相同的結果
四、自動配置原理
說完上面這些,我們來看一下,Spring Boot是如何實現自動裝配的
引導加載自動配置類
我們先點進@SpringBootApplication
的源碼中去,發現它其實是3個註解的合成註解:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
@SpringBootConfiguration
其中,@SpringBootConfiguration
的源碼底層使用瞭@Configuration
註解,說明它其實也是一個配置類,隻不過它相當於是一個主配置。
@ComponentScan
根據我們之前的學習,這個註解其實就是一個組件掃描器的作用,是Spring的註解
@EnableAutoConfiguration(核心)
最關鍵最核心的註解就是這個,@EnableAutoConfiguration
@AutoConfigurationPackage
我們點進它的源碼裡面去,發現它底層調用瞭一個叫@AutoConfigurationPackage
的註解,翻譯過來就是自動配置包,它指定瞭默認的包規則
再繼續查看它的底層源碼,發現它導入瞭一個叫Register
的組件
點進去,給它打上一個斷點,來看一下到底是如何運作的
代碼現在在這裡停住瞭,讓我們來看一下這一行代碼裡面,發現它能夠找到我們所在的包名,由此我們也就能知道為什麼 Spring Boot 能夠將指定的一個包下的所有組件導入進來瞭,
@Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration
註解下還有一個註解,它是幹什麼的呢?讓我們看一下源碼就知道瞭
在AutoConfigurationImportSelector
類下面我們找到瞭一個方法getAutoConfigurationEntry(annotationMetadata)
,它是用來給容器中批量導入一些組件的
給他打一個斷點,看看到底加入瞭些什麼組件進容器裡
說明這 131 個對象都是要加入到容器中的,並且都存儲在瞭一個List集合當中 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes)
為什麼會是131個呢? 其實是Spring Boot 裡面寫死瞭,一啟動就要給容器中加載的所有配置類
打開spring-boot-autoconfigure-2.5.0.RELEASE.jar下META-INF/spring.factories
位置的文件,讓我們來看一下源碼是怎麼寫的
這裡寫瞭一大堆 xxxxAutoConfiguration 的配置類,從26行開始,到156行結束,剛好是 131 個。
雖然我們 131 個場景的所有自動配置啟動的時候默認全部加載,但並不是都會生效的,比如 AOP 的部分功能就需要你導入 aspectj 相關的包才能生效。它是按照條件裝配規則(@Conditional
),最終會按需配置。
如圖, aspectj 是爆紅的
除瞭AOP之外,還有一些類也是沒有生效的(比如CacheAutoConfiguration
),這裡就不再贅述瞭,感興趣的同學可以去看源碼瞭解一下。
五、修改默認配置(約定大於配置)
SpringBoot 默認會在底層配好所有的組件。但是如果用戶自己配置瞭以用戶的優先
以 SpringMVC 中的文件上傳解析器為例,他在容器中的默認名字是multipartResolver
,但是我們寫代碼的時候可能不知道底層源碼裡面給他的默認名字是這個,我們給他起瞭另外一個名字,這個時候 SpringBoot 就會去容器中找到你所配置的那個組件,並且返回那個組件,也就是下面這段代碼:
這是為瞭防止有些用戶配置的文件上傳解析器不符合規范。
六、總結
- SpringBoot先加載所有的自動配置類 xxxxxAutoConfiguration
- 每個自動配置類按照條件進行生效,默認都會綁定配置文件指定的值。xxxxProperties裡面拿。xxxProperties和配置文件進行瞭綁定
- 生效的配置類就會給容器中裝配很多組件
- 隻要容器中有這些組件,相當於這些功能就有瞭
- 定制化配置
- 用戶直接自己@Bean替換底層的組件
- 用戶去看這個組件是獲取的配置文件什麼值就去修改。
整個的流程:xxxxxAutoConfiguration —> 組件 —> xxxxProperties裡面拿值 —-> application.properties
所以,當我們需要修改組件的配置的時候,隻需要在application.properties裡面進行配置即可
到此這篇關於java進階之瞭解SpringBoot的配置原理的文章就介紹到這瞭,更多相關SpringBoot的配置原理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- springboot中一些比較常用的註解總結
- JAVA Spring Boot 自動配置實現原理詳解
- Java經典面試題匯總:Spring Boot
- SpringBoot自動配置特點與原理詳細分析
- Springboot常用註解及配置文件加載順序詳解