Spring 中如何控制 Bean 的加載順序?
如果你脫口而出說添加 @Order 注解或者是實現(xiàn) Ordered 接口,那么恭喜,你掉坑了。
一、@Order 注解和 Ordered 接口
在 Spring 框架中,@Order 是一個非常實用的元注解,它位于 spring-core 包下,主要用于控制某些特定上下文中組件的執(zhí)行順序或排序,但它并不直接控制 Bean 的初始化順序。
1.1 用途
@Order 注解或者是 Ordered 接口,常見的用途主要是兩種:
- 定義執(zhí)行順序:當多個組件(如攔截器、過濾器、監(jiān)聽器等)需要按照特定的順序執(zhí)行時,@Order 注解可以用來指定這些組件的執(zhí)行優(yōu)先級。數(shù)值越小,優(yōu)先級越高,相應的組件會更早被執(zhí)行或被放置在集合的前面(@Order 注解接受一個整數(shù)值,這個值可以是負數(shù)、零或正數(shù)。Spring 框架提供了 Ordered.HIGHEST_PRECEDENCE(默認最低優(yōu)先級)和 Ordered.LOWEST_PRECEDENCE(默認最高優(yōu)先級)常量,分別對應于 Integer.MIN_VALUE和 Integer.MAX_VALUE,可以方便地設定優(yōu)先級。)。
- 集合排序:當相同類型的組件被自動裝配到一個集合中時,@Order 注解會影響它們在這個集合中的排列順序。
1.2 使用場景
經典使用場景。
攔截器排序
在 Spring MVC 中,可以使用 @Order 來控制攔截器的執(zhí)行順序。
Spring Security Filters(過濾器)
在 Spring Security 中,過濾器鏈的順序通過 @Order 來定義,確保正確的安全處理流程。這個在松哥最近的教程中和大家詳細介紹過了。
圖片
Event Listeners(事件監(jiān)聽器)
當有多個監(jiān)聽同一事件的監(jiān)聽器時,可以通過 @Order 來控制它們的觸發(fā)順序。
Bean 的集合注入
當一個 Bean 依賴于一個特定類型的 Bean 集合時,帶有 @Order 注解的 Bean 將按照指定順序被注入。
可以看到,@Order 注解的使用場景中,主要是相同類型的 Bean 存在多個時,這多個 Bean 的執(zhí)行順序可以通過 @Order 注解或者實現(xiàn) Ordered 接口來確定。
但是?。。?/p>
@Order 注解不控制初始化和加載:@Order 注解不直接影響 Bean 的創(chuàng)建和初始化過程,這些由 Spring IoC 容器基于依賴關系和配置來決定。Spring IoC 容器根據依賴關系圖來決定 Bean 的初始化順序,而不是依據 @Order 注解。依賴關系決定了哪些 Bean 需要在其他 Bean 初始化之前被創(chuàng)建。
二、如何設置 Bean 的加載順序?
有兩種方式來設置 Bean 的加載順序。
2.1 @DependsOn
@DependsOn 是 Spring 框架提供的一個注解,用于指示 Spring 容器在初始化一個 Bean 之前,必須先初始化其依賴的其他 Bean。這個注解可以幫助解決 Bean 間的依賴關系,確保依賴的 Bean 已經準備就緒。
@DependsOn 可以放在任何一個 Spring 管理的 Bean 定義上,包括但不限于配置類、服務類、數(shù)據訪問對象等。其語法如下:
@DependsOn({"beanName1", "beanName2", ...})
public class MyBean {
// ...
}
在這個例子中,MyBean 類聲明了它依賴于名為 beanName1 和 beanName2 的 Bean。這意味著,當 Spring 容器創(chuàng)建 MyBean 的實例時,它會首先確保 beanName1 和 beanName2 已經被正確初始化。
相關的源碼在 AbstractBeanFactory#doGetBean 方法中:
圖片
在創(chuàng)建的 Bean 的時候,先檢查該 Bean 是否依賴了其他 Bean,如果依賴了,則先把其他 Bean 創(chuàng)建出來,然后再繼續(xù)創(chuàng)建當前 Bean,這樣就確保了 Bean 的加載順序。
如果小伙伴們對這塊的完整流程感興趣,可以看松哥錄制的 Spring源碼分析。
2.2 BeanFactoryPostProcessor
第二種方式就是利用 BeanFactoryPostProcessor,BeanFactoryPostProcessor 的執(zhí)行時機比較早,從下面這張流程圖中可以看到,BeanFactoryPostProcessor 在正常的 Bean 初始化之前就執(zhí)行了。
圖片
那么對于想要提前初始化的 Bean,我們可以在 BeanFactoryPostProcessor 中手動調用,類似下面這樣:
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//想要提前初始化的 Bean 在這里執(zhí)行
beanFactory.getBean("serviceB");
}
}
三、小結
多個相同類型的 Bean 該如何確保其執(zhí)行順序?這個靠 @Order 注解或者 Ordered 接口來解決。但是這兩者并不能解決 Bean 的加載順序。Bean 的加載順序有兩種方式可以調整:
- 通過 @DependsOn 注解加載。
- 手動在 BeanFactoryPostProcessor#postProcessBeanFactory 方法中提前調用 getBean 方法去初始化 Bean。
如果小伙伴們想要徹底掌握 Spring 源碼,那么不妨看看松哥錄制的 Spring 源碼視頻教程。
這套視頻教程基于目前最新的 Spring6 錄制,一共有 204 集約 40 個小時,從 Spring 基本用法講起,一直講到源碼分析。
以下是完整視頻目錄:
├── 01.基礎用法
│ ├── 01.IoC基礎
│ │ ├── 01.Spring框架介紹-核心容器.mp4
│ │ ├── 02.Spring框架介紹-數(shù)據訪問和通信.mp4
│ │ ├── 03.Spring框架介紹-Web模塊.mp4
│ │ ├── 04.Spring框架介紹-AOP和測試.mp4
│ │ ├── 05.什么是IoC.mp4
│ │ ├── 06.自定義一個簡單的IoC.mp4
│ │ ├── 07.向Spring容器注冊Bean.mp4
│ │ ├── 08.從Spring容器獲取Bean.mp4
│ │ ├── 09.id屬性和name屬性.mp4
│ │ ├── 10.基本屬性注入.mp4
│ │ ├── 11.復雜屬性注入.mp4
│ │ ├── 12.構造器注入.mp4
│ │ ├── 13.p名稱空間注入.mp4
│ │ ├── 14.屬性自動注入.mp4
│ │ ├── 15.配置文件加載.mp4
│ │ ├── 16.Java代碼配置IoC.mp4
│ │ ├── 17.BeanName自動生成原理.mp4
│ │ ├── 18.id和name屬性處理原理.mp4
│ │ ├── 19.Bean 的作用域.mp4
│ │ ├── 20.singleton 和 prototype 的區(qū)別.mp4
│ │ ├── 21.條件注解詳解.mp4
│ │ ├── 22.多環(huán)境切換.mp4
│ │ ├── 23.Profile原理分析.mp4
│ │ ├── 24.自定義Profile.mp4
│ │ ├── 25.Bean的依賴關系.mp4
│ │ ├── 26.FactoryBean用法.mp4
│ │ ├── 27.抽取Bean的公共屬性.mp4
│ │ ├── 28.父子容器問題.mp4
│ │ ├── 29.@Configuration注解的作用.mp4
│ │ ├── 30.Bean自動掃描.mp4
│ │ └── 31.屬性值注入.mp4
│ ├── 02.AOP基礎
│ │ ├── 01.靜態(tài)代理.mp4
│ │ ├── 02.編譯時增強和運行時增強.mp4
│ │ ├── 03.編譯時增強.mp4
│ │ ├── 04.運行時增強-JDK.mp4
│ │ ├── 05.運行時增強-CGLIB.mp4
│ │ ├── 06.什么是Spring AOP.mp4
│ │ ├── 07.SpringAOP和AspectJAOP.mp4
│ │ ├── 08.Spring AOP核心概念.mp4
│ │ ├── 09.Spring AOP入門用法.mp4
│ │ ├── 10.五種通知.mp4
│ │ ├── 11.Java代碼配置AOP.mp4
│ │ ├── 12.SpringAOP底層代理.mp4
│ │ └── 13.通過注解定義AOP攔截規(guī)則.mp4
│ ├── 03.JdbcTemplate
│ │ ├── 01.JdbcTemplate基本操作.mp4
│ │ ├── 02.增刪改查.mp4
│ │ └── 03.通過變量名傳遞參數(shù).mp4
│ └── 04.事務
│ ├── 01.事務簡介.mp4
│ ├── 02.編程式事務.mp4
│ ├── 03.XML配置聲明式事務.mp4
│ ├── 04.Java配置聲明式事務.mp4
│ ├── 05.Java+XML配置聲明式事務.mp4
│ ├── 06.自定義事務注解.mp4
│ ├── 07.事務屬性-隔離級別.mp4
│ ├── 08.事務屬性-傳播性.mp4
│ ├── 09.事務屬性-傳播性2.mp4
│ ├── 10.事務屬性-回滾規(guī)則.mp4
│ ├── 11.事務屬性-只讀事務.mp4
│ ├── 12.事務屬性-超時時間.mp4
│ └── 13.事務失效的場景.mp4
└── 02.進階用法
├── 001.通過name屬性定義別名.mp4
├── 002.通過alias標簽定義別名.mp4
├── 003.別名處理接口AliasRegistry.mp4
├── 004.aliasMap變量.mp4
├── 005.allowAliasOverriding.mp4
├── 006.hasAlias.mp4
├── 007.removeAlias和isAlias.mp4
├── 008.getAliases.mp4
├── 009.checkForAliasCircle.mp4
├── 010.canonicalName.mp4
├── 011.registerAlias.mp4
├── 012.resolveAliases.mp4
├── 013.name屬性解析原理.mp4
├── 014.alias標簽解析.mp4
├── 015.DefaultListableBeanFactory.mp4
├── 016.StaticListableBeanFactory.mp4
├── 017.SimpleJndiBeanFactory.mp4
├── 018.ApplicationContext.mp4
├── 019.FactoryBean&SmartFactoryBean.mp4
├── 020.FactoryBean處理思路.mp4
├── 021.Bean提前加載流程分析.mp4
├── 022.isFactoryBean方法分析.mp4
├── 023.getBean中&的處理邏輯.mp4
├── 024.BeanDefinition簡介.mp4
├── 025.RootBeanDefinition.mp4
├── 026.ChildBeanDefinition.mp4
├── 027.GenericBeanDefinition.mp4
├── 028.AnnotatedGenericBeanDefinition.mp4
├── 029.ScannedGenericBeanDefinition.mp4
├── 030.ConfigurationClassBeanDefinition.mp4
├── 031.CreateFromClassBeanDefinition.mp4
├── 032.ClassDerivedBeanDefinition.mp4
├── 033.加載XML配置的兩種方式.mp4
├── 034.BeanDefinitionReader.mp4
├── 035.PropertiesBeanDefinitionReader.mp4
├── 036.XmlBeanDefinitionReader.mp4
├── 037.配置類解析為BeanDefinition.mp4
├── 038.@Scope注解高級用法.mp4
├── 039.編程式AOP.mp4
├── 040.編程式AOP之target方法分析.mp4
├── 041.TargetSource體系結構.mp4
├── 042.SimpleBeanTargetSource.mp4
├── 043.自定義TargetSource.mp4
├── 044.引介增強.mp4
├── 045.啟動類上的@Scope注解代理原理.mp4
├── 046.常規(guī)類上的@Scope注解.mp4
├── 047.Java配置中的@Scope代理.mp4
├── 048.導入配置類上的@Scope注解.mp4
├── 049.@Configuration注解存在的意義是什么.mp4
├── 050.@Configuration-Vs-@Component.mp4
├── 051.@Configuration原理分析.mp4
├── 052.@Configuration的兩種模式Full和Lite.mp4
├── 053.詳細演示Full和Lite模式.mp4
├── 054.Full模式和Lite模式特點總結.mp4
├── 055.@Configuration注解解析源碼分析.mp4
├── 056.@Configuration注解解析源碼分析-2.mp4
├── 057.條件注解高級用法.mp4
├── 058.條件注解高級用法補充.mp4
├── 059.條件注解原理分析.mp4
├── 060.beanName自動生成場景.mp4
├── 061.beanName生成器分析.mp4
├── 062.AnnotationBeanNameGenerator.mp4
├── 063.BeanFactoryPostProcessor和BeanPostProcessor.mp4
├── 064.BeanFactoryPostProcessor案例.mp4
├── 065.BeanFactoryPostProcessor典型應用場景.mp4
├── 066.Properties加載原理分析.mp4
├── 067.BeanDefinitionRegistryPostProcessor.mp4
├── 068.BeanFactoryPostProcessor作用時機分析.mp4
├── 069.BeanPostProcessor接口分析.mp4
├── 070.BeanPostProcessor實踐.mp4
├── 071.BeanPostProcessor實現(xiàn)AOP.mp4
├── 072.MergedBeanDefinitionPostProcessor.mp4
├── 073.BeanPostProcessor原理分析.mp4
├── 074.合并BeanDefinition原理分析.mp4
├── 075.合并BeanDefinition源碼分析.mp4
├── 076.父子容器原理分析.mp4
├── 077.事件基本用法.mp4
├── 078.事件是阻塞還是非阻塞.mp4
├── 079.手動注冊事件監(jiān)聽器.mp4
├── 080.事件三大組件.mp4
├── 081.Java類定義的事件監(jiān)聽器.mp4
├── 082.Java類定義的事件監(jiān)聽器-補充.mp4
├── 083.通過Java注解定義的事件監(jiān)聽器.mp4
├── 084.ApplicationEventPublisher.mp4
├── 085.ApplicationEventMulticaster.mp4
├── 086.Spring事件補充.mp4
├── 087.Java國際化.mp4
├── 088.Spring國際化.mp4
├── 089.MessageSource層級關系.mp4
├── 090.getMessage方法原理分析.mp4
├── 091.解析無參的key.mp4
├── 092.解析有參key.mp4
├── 093.Lifecycle基本用法.mp4
├── 094.SmartLifecycle基本用法.mp4
├── 095.Lifecycle中start方法執(zhí)行原理.mp4
├── 096.Lifecycle中stop方法執(zhí)行原理.mp4
├── 097.編程式AOP.mp4
├── 098.動態(tài)代理對象創(chuàng)建流程.mp4
├── 099.JDK和CGLIB動態(tài)代理分析.mp4
├── 100.Advisor詳解.mp4
├── 101.自定義切面對象Advisor.mp4
├── 102.重載方法攔截規(guī)則.mp4
├── 103.攔截所有方法.mp4
├── 104.默認配置的Pointcut對象.mp4
├── 105.Pointcut經典寫法.mp4
├── 106.Pointcut分類.mp4
├── 107.StaticMethodMatcherPointcut.mp4
├── 108.StaticMethodMatcherPointcut-2.mp4
├── 109.DynamicMethodMatcherPointcut.mp4
├── 110.AnnotationMatchingPointcut.mp4
├── 111.ControlFlowPointcut.mp4
├── 112.ComposablePointcut.mp4
├── 113.提前返回Bean的機會.mp4
├── 114.提前AOP的機會.mp4
├── 115.理解兩個關鍵變量.mp4
├── 116.特殊的beanName.mp4
├── 117.自定義TargetSourceCreator.mp4
├── 118.提前AOP實踐.mp4
├── 119.AOP創(chuàng)建的契機.mp4
├── 120.AOP對象生成整體思路.mp4
├── 121.AOP對象創(chuàng)建詳細流程分析.mp4
├── 122.Bean的獲取思路.mp4
├── 123.getBean方法整體思路分析.mp4
├── 124.從三級緩存中加載Bean.mp4
├── 125.從parent中加載Bean.mp4
├── 126.檢查@DependOn注解.mp4
├── 127.不同Scope獲取Bean實例.mp4
├── 128.生成的Bean類型檢查.mp4
├── 129.createBean方法分析.mp4
├── 130.doCreateBean-1.mp4
├── 131.循環(huán)依賴-二級緩存.mp4
├── 132.循環(huán)依賴-三級緩存.mp4
├── 133.循環(huán)依賴-三級緩存-補充.mp4
├── 134.循環(huán)依賴源碼分析-1.mp4
├── 135.循環(huán)依賴源碼分析-2.mp4
├── 136.循環(huán)依賴源碼分析-3.mp4
├── 137.Bean的生命周期-1.mp4
├── 138.Bean的生命周期-2.mp4
├── 139.Bean的生命周期-3.mp4
├── 140.Bean的實例化-1.mp4
├── 141.Bean的實例化-2.mp4
├── 142.Bean的屬性注入.mp4
├── 143.getBean方法.mp4
└── 144.小結.mp4