使用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对象,通过第二个构造方法自动注入而引发问题。
    使用ApplicationContextInitializer在Spring容器refresh前手工创建bean_第1张图片
    编写完LizardDataShardingGenericApplicationContextInitializer,我们配置一下spring.factories,让Spring Boot容器启动时,自动加载该类并执行容器初始化自定义逻辑。文件路径:projectPath/src/main/resources/META-INF/spring.factories
    在这里插入图片描述

你可能感兴趣的:(Spring,Boot)