切換語言為:簡體
Spring Bean的載入順序是如何被控制的?

Spring Bean的載入順序是如何被控制的?

  • 爱糖宝
  • 2024-05-15
  • 2145
  • 0
  • 0

前言

正常情況下,Spring 容器載入 Bean 的順序是不確定的,那麼我們如果需要按順序載入 Bean 時應如何操作?本文將詳細講述我們如何才能控制 Bean 的載入順序。


場景

我建立了 4 個 Class 檔案,分別命名為

  1. FirstInitialization

  2. SecondInitialization

  3. ThirdInitialization

  4. ForthInitialization

我希望這 4 個類按照 1、2、3、4 的順序載入。

如下圖,直接載入的話,順序是 1、4、2、3,並不能達到要求。

Spring Bean的載入順序是如何被控制的?

如何控制

注意:網上很多文章說Order註解或Ordered介面可以控制 Bean 的載入順序,其是並不能,它們的作用是定義 Spring IOC 容器中 Bean 定義類的執行順序的優先順序,並不是定義載入順序。

使用@DependsOn 註解

在需要調整順序的類上依次加@DependsOn註解,缺點是類過多的時候需要一個個加註解,且不好維護。

@Component
public class FirstInitialization {
 
    @PostConstruct
    public void init(){
        System.out.println("我是第一個載入!");
    }
 
}
@Component
@DependsOn("firstInitialization")
public class SecondInitialization {
 
    @PostConstruct
    public void init(){
        System.out.println("我是第二個載入!");
    }
 
}
@Component
@DependsOn("secondInitialization")
public class ThirdInitialization {
 
    @PostConstruct
    public void init(){
        System.out.println("我是第三個載入!");
    }
 
}
@Component
@DependsOn("thirdInitialization")
public class ForthInitialization {
 
    @PostConstruct
    public void init(){
        System.out.println("我是第四個載入!");
    }
 
}

執行結果如下

Spring Bean的載入順序是如何被控制的?

基於 ApplicationContextInitializer 介面

介面簡介

這裏我簡單介紹一個這個介面的用處, 等到整理到相關原始碼的時候再詳細介紹。

ApplicationContextInitializer介面是在 Spring 容器重新整理之前執行的一個回撥函式。

執行時機:

  1. Spring 內部執行ConfigurableApplicationContext#refresh()方法前;

  2. SpringBoot 執行run()方法前。


一般有什麼用呢?

在 SpringBoot 應用中 Classpath 上會有很多 jar 包,有些 jar 包需要在refresh()呼叫前對應用上下文做一些初始化動作,因此會提供ApplicationContextInitializer介面的實現類,放在如下圖的檔案中,這樣會被SpringApplication#initialize發現,然後完成對應初始化。

Spring Bean的載入順序是如何被控制的?

實現步驟

首先建立一個類繼承ApplicationContextInitializer介面。

public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
 
        //將自定義的BeanFactoryPostProcessor實現類儲存到ApplicationContext中
        applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
    }
}

建立`META-INF/spring.factories`檔案。

Spring Bean的載入順序是如何被控制的?


自定義`BeanDefinitionRegistryPostProcessor`。

/**
 * BeanFactoryPostProcessor的子類
 * 允許開發人員在Bean定義註冊之前和之後對BeanDefinition進行自定義處理,例如新增,修改或刪除Bean定義等。
 */
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
 
    // 初始化需要排序的類,這裏要保證插入順序只能用LinkedHashMap
    private static final Map<String, Class> ORDER_BEAN_MAP = new LinkedHashMap<>() {
        {
            put("firstInitialization", FirstInitialization.class);
            put("secondInitialization", SecondInitialization.class);
            put("thirdInitialization", ThirdInitialization.class);
            put("forthInitialization", ForthInitialization.class);
        }
    };
 
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Optional.ofNullable(ORDER_BEAN_MAP.keySet()).orElse(new HashSet<>()).stream()
                .forEach(beanName -> {
                    // 初始化一個 Bean 定義
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition().getBeanDefinition();
 
                    // 按順序註冊每個Bean
                    beanDefinition.setBeanClass(ORDER_BEAN_MAP.get(beanName));
                    registry.registerBeanDefinition(beanName, beanDefinition);
                });
    }
}

執行結果如下

Spring Bean的載入順序是如何被控制的?

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.