前言
正常情況下,Spring 容器載入 Bean 的順序是不確定的,那麼我們如果需要按順序載入 Bean 時應如何操作?本文將詳細講述我們如何才能控制 Bean 的載入順序。
場景
我建立了 4 個 Class 檔案,分別命名為
- FirstInitialization
- SecondInitialization
- ThirdInitialization
- ForthInitialization
我希望這 4 個類按照 1、2、3、4 的順序載入。
如下圖,直接載入的話,順序是 1、4、2、3,並不能達到要求。
如何控制
注意:網上很多文章說
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("我是第四個載入!");
}
}
執行結果如下
基於 ApplicationContextInitializer 介面
介面簡介
這裡我簡單介紹一個這個介面的用處, 等到整理到相關原始碼的時候再詳細介紹。
ApplicationContextInitializer
介面是在 Spring 容器重新整理之前執行的一個回撥函式。
執行時機:
- Spring 內部執行
ConfigurableApplicationContext#refresh()
方法前;- SpringBoot 執行
run()
方法前。
一般有什麼用呢?
在 SpringBoot 應用中 Classpath 上會有很多 jar 包,有些 jar 包需要在refresh()
呼叫前對應用上下文做一些初始化動作,因此會提供ApplicationContextInitializer
介面的實現類,放在如下圖的檔案中,這樣會被SpringApplication#initialize
發現,然後完成對應初始化。
實現步驟
首先建立一個類繼承ApplicationContextInitializer
介面。
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
//將自定義的BeanFactoryPostProcessor實現類儲存到ApplicationContext中
applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
}
}
建立`META-INF/spring.factories`檔案。
自定義`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);
});
}
}
執行結果如下