如何正確控制springboot中bean的加載順序小結(jié)篇
1.為什么需要控制加載順序
springboot
遵從約定大于配置的原則,極大程度的解決了配置繁瑣的問題。在此基礎(chǔ)上,又提供了spi機(jī)制,用spring.factories
可以完成一個(gè)小組件的自動(dòng)裝配功能。
在一般業(yè)務(wù)場(chǎng)景,可能你不大關(guān)心一個(gè)bean是如何被注冊(cè)進(jìn)spring容器的。只需要把需要注冊(cè)進(jìn)容器的bean聲明為@Component
即可,spring會(huì)自動(dòng)掃描到這個(gè)Bean完成初始化并加載到spring上下文容器。
而當(dāng)你在項(xiàng)目啟動(dòng)時(shí)需要提前做一個(gè)業(yè)務(wù)的初始化工作時(shí),或者你正在開發(fā)某個(gè)中間件需要完成自動(dòng)裝配時(shí)。你會(huì)聲明自己的Configuration類,但是可能你面對(duì)的是好幾個(gè)有互相依賴的Bean。如果不加以控制,這時(shí)候可能會(huì)報(bào)找不到依賴的錯(cuò)誤。
但是你明明已經(jīng)把相關(guān)的Bean都注冊(cè)進(jìn)spring上下文了呀。這時(shí)候你需要通過一些手段來控制springboot中的bean加載順序。
2.幾個(gè)誤區(qū)
在正式說如何控制加載順序之前,先說2個(gè)誤區(qū)。
在標(biāo)注了@Configuration
的類中,寫在前面的@Bean一定會(huì)被先注冊(cè)
這個(gè)不存在的,spring在以前xml的時(shí)代,也不存在寫在前面一定會(huì)被先加載的邏輯。因?yàn)閤ml不是漸進(jìn)的加載,而是全部parse好,再進(jìn)行依賴分析和注冊(cè)。到了springboot中,只是省去了xml被parse成spring內(nèi)部對(duì)象的這一過程,但是加載方式并沒有大的改變。
利用@Order
這個(gè)標(biāo)注能進(jìn)行加載順序的控制
嚴(yán)格的說,不是所有的Bean都可以通過@Order
這個(gè)標(biāo)注進(jìn)行順序的控制。你把@Order
這個(gè)標(biāo)注加在普通的方法上或者類上一點(diǎn)鳥用都沒有。
那@Order
能控制哪些bean的加載順序呢,我們先看看官方的解釋:
{@code @Order} defines the sort order for an annotated component. Since Spring 4.0, annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their {@code @Bean} method). While such order values may influence priorities at injection points, please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).
最開始@Order
注解用于切面的優(yōu)先級(jí)指定;在 4.0 之后對(duì)它的功能進(jìn)行了增強(qiáng),支持集合的注入時(shí),指定集合中 bean 的順序,并且特別指出了,它對(duì)于但實(shí)例的 bean 之間的順序,沒有任何影響。
目前用的比較多的有以下3點(diǎn):
@Aspect
ApplicationListener
CommandLineRunner
3.如何控制
@DependsOn
注解可以用來控制bean的創(chuàng)建順序,該注解用于聲明當(dāng)前bean依賴于另外一個(gè)bean。所依賴的bean會(huì)被容器確保在當(dāng)前bean實(shí)例化之前被實(shí)例化。
示例:
@Configuration public class BeanOrderConfiguration { @Bean @DependsOn("beanB") public BeanA beanA(){ System.out.println("bean A init"); return new BeanA(); } @Bean public BeanB beanB(){ System.out.println("bean B init"); return new BeanB(); } @Bean @DependsOn({"beanD","beanE"}) public BeanC beanC(){ System.out.println("bean C init"); return new BeanC(); } @Bean @DependsOn("beanE") public BeanD beanD(){ System.out.println("bean D init"); return new BeanD(); } @Bean public BeanE beanE(){ System.out.println("bean E init"); return new BeanE(); } }
以上代碼bean的加載順序?yàn)椋?/p>
bean B init
bean A init
bean E init
bean D init
bean C init
@DependsOn
的使用:
- 直接或者間接標(biāo)注在帶有
@Component
注解的類上面; - 直接或者間接標(biāo)注在帶有
@Bean
注解的方法上面; - 使用
@DependsOn
注解到類層面僅僅在使用 component-scanning 方式時(shí)才有效,如果帶有@DependsOn
注解的類通過XML方式使用,該注解會(huì)被忽略,<bean depends-on="..."/>
這種方式會(huì)生效。
3.2 參數(shù)注入
在@Bean
標(biāo)注的方法上,如果你傳入了參數(shù),springboot會(huì)自動(dòng)會(huì)為這個(gè)參數(shù)在spring上下文里尋找這個(gè)類型的引用。并先初始化這個(gè)類的實(shí)例。
利用此特性,我們也可以控制bean的加載順序。
示例:
@Bean public BeanA beanA(BeanB demoB){ System.out.println("bean A init"); return new BeanA(); } @Bean public BeanB beanB(){ System.out.println("bean B init"); return new BeanB(); }
以上結(jié)果,beanB先于beanA被初始化加載。
需要注意的是,springboot會(huì)按類型去尋找。如果這個(gè)類型有多個(gè)實(shí)例被注冊(cè)到spring上下文,那你就需要加上@Qualifier("Bean的名稱")
來指定
3.3 利用bean的生命周期中的擴(kuò)展點(diǎn)
在spring體系中,從容器到Bean實(shí)例化&初始化都是有生命周期的,并且提供了很多的擴(kuò)展點(diǎn),允許你在這些步驟時(shí)進(jìn)行邏輯的擴(kuò)展。
這些可擴(kuò)展點(diǎn)的加載順序由spring自己控制,大多數(shù)是無法進(jìn)行干預(yù)的。我們可以利用這一點(diǎn),擴(kuò)展spring的擴(kuò)展點(diǎn)。在相應(yīng)的擴(kuò)展點(diǎn)加入自己的業(yè)務(wù)初始化代碼。從來達(dá)到順序的控制。
具體關(guān)于spring容器中大部分的可擴(kuò)展點(diǎn)的分析,之前已經(jīng)寫了一篇文章詳細(xì)介紹了:《Springboot啟動(dòng)擴(kuò)展點(diǎn)超詳細(xì)總結(jié),再也不怕面試官問了》。
3.4 @AutoConfigureOrder
這個(gè)注解用來指定配置文件的加載順序。但是在實(shí)際測(cè)試中發(fā)現(xiàn),以下這樣使用是不生效的:
@Configuration @AutoConfigureOrder(2) public class BeanOrderConfiguration1 { @Bean public BeanA beanA(){ System.out.println("bean A init"); return new BeanA(); } } @Configuration @AutoConfigureOrder(1) public class BeanOrderConfiguration2 { @Bean public BeanB beanB(){ System.out.println("bean B init"); return new BeanB(); } }
無論你2個(gè)數(shù)字填多少,都不會(huì)改變其加載順序結(jié)果。
那這個(gè)@AutoConfigureOrder
到底是如何使用的呢。
經(jīng)過測(cè)試發(fā)現(xiàn),@AutoConfigureOrder
只能改變外部依賴的@Configuration
的順序。如何理解是外部依賴呢。
能被你工程內(nèi)部scan到的包,都是內(nèi)部的Configuration,而spring引入外部的Configuration,都是通過spring特有的spi文件:spring.factories
換句話說,@AutoConfigureOrder
能改變spring.factories
中的@Configuration
的順序。
具體使用方式:
@Configuration @AutoConfigureOrder(10) public class BeanOrderConfiguration1 { @Bean public BeanA beanA(){ System.out.println("bean A init"); return new BeanA(); } } @Configuration @AutoConfigureOrder(1) public class BeanOrderConfiguration2 { @Bean public BeanB beanB(){ System.out.println("bean B init"); return new BeanB(); } }
spring.factories
:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.BeanOrderConfiguration1,\ com.example.demo.BeanOrderConfiguration2
4.總結(jié)
其實(shí)在工作中,我相信很多人碰到過復(fù)雜的依賴關(guān)系的bean加載,把這種不確定性交給spring去做,還不如我們自己去控制,這樣在閱讀代碼的時(shí)候 ,也能輕易看出bean之間的依賴先后順序。
到此這篇關(guān)于如何正確控制springboot中bean的加載順序總結(jié)的文章就介紹到這了,更多相關(guān)springboot中bean的加載順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java ArrayList與LinkedList及HashMap容器的用法區(qū)別
這篇文章主要介紹了Java ArrayList與LinkedList及HashMap容器的用法區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-07-07深入理解java中的synchronized關(guān)鍵字
這篇文章主要介紹了java中的synchronized關(guān)鍵字,有需要的朋友可以參考一下2013-12-12Java啟動(dòng)參數(shù)(-,?-X,?-XX參數(shù))的使用
本文主要介紹了Java啟動(dòng)參數(shù)(-,?-X,?-XX參數(shù))的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06Java計(jì)算兩個(gè)時(shí)間相差的秒數(shù)怎么算
這篇文章主要介紹了Java計(jì)算兩個(gè)時(shí)間相差的秒數(shù),通過實(shí)例代碼補(bǔ)充介紹了Java 獲取兩個(gè)時(shí)間的時(shí)間差(時(shí)、分、秒)問題,感興趣的朋友跟隨小編一起看看吧2024-03-03