前言
正常情況下,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); }); } }
執行結果如下