前言
最近在基於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