使用ApplicationContextInitializer在Spring容器refresh前手工創建bean

前言

最近在基於Spring Boot做一個分庫分表的組件,希望在容器refresh前,創建數據源DataSource等。不過,這個不是本文的重點,我會之後單獨寫一篇文章來說明這個組件。

如何做

LizardDataShardingGenericApplicationContextInitializer.java

public class LizardDataShardingGenericApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
    @Override
	public void initialize(GenericApplicationContext applicationContext) {
		// GenericApplicationContext這個類型不常用,我們基於它可以實現在Spring容器refresh前,手工動態創建Bean
		// 在需要創建bean的地方,調用registerBean方法即可
		// 參數1:beanName,參數2:Bean類型,參數3:實例化Bean的方法(構造函數),參數4:設置bean的屬性,要和set方法後的首字母小寫對應
	applicationContext.registerBean(BEAN_TRANSACTION_INTERCEPTOR,TransactionInterceptor.class,TransactionInterceptor::new, (BeanDefinitionCustomizer) beanDefinition -> {
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		beanDefinition.getPropertyValues().addPropertyValue("transactionManagerBeanName", transactionManager);
		beanDefinition.getPropertyValues().addPropertyValue("transactionAttributeSource", new RuntimeBeanReference(BEAN_ANNOTATION_TRANSACTION_SOURCE));
		});
	}
}
  • 如果bean屬性的值,也是使用動態創建的Bean,則直接通過RuntimeBeanReference(bean名稱來引用)。因爲在refresh前,並沒有真正的創建Bean。上述代碼相當於傳統的bean標籤
  • 如果Bean有多個構造方法,一定要在第三個參數中指定使用空構造器,並通過beanDefinition來設置屬性值。因爲在Spring4開始,默認使用構造器自動注入;如果你創建的Bean有多個含參構造器,Spring會注入類型相符的Bean來進行類的實例化。
    例如案例中的TransactionInterceptor,它包含如下構造函數。如果不指定初始化方式,Spring會將容器中的TransactionManager和Properties對象,通過第二個構造方法自動注入而引發問題。
    在這裏插入圖片描述
    編寫完LizardDataShardingGenericApplicationContextInitializer,我們配置一下spring.factories,讓Spring Boot容器啓動時,自動加載該類並執行容器初始化自定義邏輯。文件路徑:projectPath/src/main/resources/META-INF/spring.factories
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章