Spring中如何控制bean的初始化順序詳解


假設A,B兩個 bean 都需要在初始化的時候從本地磁盤讀取文件,其中B加載的文件,依賴A中加載的全局配置文件中配置的路徑,所以需要A先於B初始化,此外A中的配置改變后也需要觸發B的重新加載邏輯,所以A,B需要注入彼此。

1. 業務中判斷和控制bean初始化順序

我們可以在業務層自己控制A,B的初始化順序,在A中設置一個“是否初始化的”標記,B初始化前檢測A是否得以初始化,如果沒有則調用A的初始化方法,所謂的check-and-act。

2. 使用DependsOn注解

Spring 中的 DependsOn 注解可以保證被依賴的bean先於當前bean被容器創建,對於上述模型,如果在B上加上注解@DependsOn({"a"}),邏輯如下:
先加載的bean A,最終通過無參構造器構造,然后,繼續屬性填充(populateBean),發現需要注入 bean B。所以轉而加載 bean B(遞歸調用 getBean())。此時發現 bean B 需要 DependsOn("a"),在保存依賴關系(為了防止循環 depends)后,調用 getBean("a"),此時會得到提前暴露的 bean A ,所以繼續 B 的加載,流程為: 初始化策略構造實例 -> 屬性填充(同樣會注入提前暴露的 bean A ) -> 調用初始化方法。

DependsOn只是保證的被依賴的bean先於當前bean被實例化,被創建,所以如果要采用這種方式實現bean初始化順序的控制,那么可以把初始化邏輯放在構造函數中,但是復雜耗時的邏輯仿造構造器中是不合適的,會影響系統啟動速度。

在這里問題的關鍵是:bean屬性的注入是在初始化方法調用之前

// 代碼位置:AbstractAutowireCapableBeanFactory.doCreateBean
// 填充 bean 的各個屬性,包括依賴注入
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
  // 調用初始化方法,如果是 InitializingBean 則先調用 afterPropertiesSet 然后調用自定義的init-method 方法
  exposedObject = initializeBean(beanName, exposedObject, mbd);
}

然后通過三級緩存來解決:
參考博文:spring 如何利用三級緩存解決循環依賴

3. 使用Spring框架擴展點

spring有很多擴展點為用戶開放,什么時候可以控制初始化順序呢,在容器加載bean之前,其中 BeanFactoryPostProcessor 可以允許我們在容器加載任何bean之前修改應用上下文中的BeanDefinition,在本例中,就可以把A的初始化邏輯放在一個BeanFactoryPostProcessor 中。

@Component
public class ABeanFactoryPostProcessor implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    A.initA();
  }

這種方式把A中的初始化邏輯放到了加載bean之前,很適合加載系統全局配置,但是這種方式中初始化邏輯不能依賴bean的狀態

4. 事件監聽器的有序性

Spring 中的 Ordered 也是一個很重要的組件,很多邏輯中都會判斷對象是否實現了 Ordered 接口,如果實現了就會先進行排序操作。比如在事件發布的時候,對獲取到的 ApplicationListener 會先進行排序。

所以可以利用事件監聽器在處理事件時的有序性,在應用上下文 refresh 完成后,分別實現A,B中對應的初始化邏輯。

這種方式就是站在事件響應的角度,上下文加載完成后,先實現A邏輯,然后實現B邏輯。

參考資料:
https://blog.csdn.net/u012045045/article/details/86100670


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM