Spring动态注册Bean

一、前言

在我们开发的过程中,使用静态配置信息,即可由Spring容器生成我们需要的Bean。但是在一些场景下,我们需要Spring容器根据我们的配置信息动态的生成Bean,这时就需要将Bean动态的注册到Spring容器中去。

一般而言,我们使用@Componment、@Service等注解,就可以向Spring容器注册Bean,下面介绍几种方式动态注入Bean到Spring容器。

具体的应用场景,可以参考我写的一个简单快捷的rocketmq-spring-boot-starter里的实现。

二、设计测试的Bean
1、普通Bean,不依赖其他Bean
public class NormalBean1 {
    public NormalBean1() {
        System.out.println("NormalBean1()");
    }

    public void print() {
        System.out.println("in NormalBean1.print()");
    }
}
2、普通Bean,依赖通过动态注册注入到容器里的Bean(即依赖1的Bean)
public class NormalBean2 {
    @Autowired
    private NormalBean1 normalBean1;
    public NormalBean2() {
        System.out.println("NormalBean2()");
    }

    public void print() {
        System.out.println("in NormalBean2.print()");
        System.out.println("调用NormalBean1.print()");
        normalBean1.print();
    }
}
3、普通Bean,依赖通过注解注入到容器里的Bean(即依赖4的Bean)
public class NormalBean3 {
    @Autowired
    private ServiceBean1 serviceBean1;

    public NormalBean3() {
        System.out.println("NormalBean3()");
    }

    public void print() {
        System.out.println("in NormalBean3.print()");
        System.out.println("调用ServiceBean1.print()");
        serviceBean1.print();
    }
}
4、使用注解(@Component、@Service)等注册的Bean,不依赖其他Bean
@Service
public class ServiceBean1 {
    public ServiceBean1() {
        System.out.println("ServiceBean1()");
    }

    public void print() {
        System.out.println("in ServiceBean1.print()");
    }
}
5、使用注解(@Component、@Service)等注册的Bean,依赖通过动态注册注入到容器里的Bean(即依赖1的Bean)
@Service
public class ServiceBean2 {
    @Autowired
    private NormalBean1 normalBean1;
    public ServiceBean2() {
        System.out.println("ServiceBean2()");
    }

    public void print() {
        System.out.println("in ServiceBean2.print()");
        System.out.println("调用NormalBean1.print()");
        normalBean1.print();
    }
}
三、动态注册

动态注册Bean,是通过BeanDefinitionBuilder、BeanDefinition、BeanDefinitionRegistry来将Bean动态的注册到容器中去。

1、继承BeanDefinitionRegistryPostProcessor
@Configuration
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        registerBean("normalBean1", NormalBean1.class, beanDefinitionRegistry);
        registerBean("normalBean2", NormalBean2.class, beanDefinitionRegistry);
        registerBean("normalBean3", NormalBean3.class, beanDefinitionRegistry);
    }

    private void registerBean(String name, Class<?> clz, BeanDefinitionRegistry beanDefinitionRegistry) {
        if (beanDefinitionRegistry.isBeanNameInUse(name)) {
            throw new RuntimeException("exist beanName");
        }
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clz);
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
		//这个方法和上面的方法作用其实是一样的,一般使用上面的方式进行动态注入
    }
}
2、普通方式1
@Configuration
public class BeanDefinitionConfiguration {

    public BeanDefinitionConfiguration(ApplicationContext applicationContext) {
        System.out.println("ManualConfiguration init");
        registerBean((ConfigurableApplicationContext) applicationContext);
    }

    private void registerBean(ConfigurableApplicationContext applicationContext) {
        registerBean(applicationContext, "normalBean1", NormalBean1.class);
        registerBean(applicationContext, "normalBean2", NormalBean2.class);
        registerBean(applicationContext, "normalBean3", NormalBean3.class);
    }

    public <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clz, Object... args) {
        if (applicationContext.containsBean(name)) {
            Object bean = applicationContext.getBean(name);
            if (bean.getClass().isAssignableFrom(clz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName重复 " + name);
            }
        }

        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clz);
        for (Object arg : args) {
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }

        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
        beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
        return applicationContext.getBean(name, clz);
    }
}
3、普通方式2
@Configuration
public class BeanDefinitionConfiguration2 implements ApplicationContextAware, InitializingBean {

    private ConfigurableApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = (ConfigurableApplicationContext) applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        registerBean(applicationContext);
    }

    private void registerBean(ConfigurableApplicationContext applicationContext) {
        registerBean(applicationContext, "normalBean1", NormalBean1.class);
        registerBean(applicationContext, "normalBean2", NormalBean2.class);
        registerBean(applicationContext, "normalBean3", NormalBean3.class);
    }

    public <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clz, Object... args) {
        if (applicationContext.containsBean(name)) {
            Object bean = applicationContext.getBean(name);
            if (bean.getClass().isAssignableFrom(clz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName重复 " + name);
            }
        }

        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clz);
        for (Object arg : args) {
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
        beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
        return applicationContext.getBean(name, clz);
    }
}
##### 3、普通方式3
```java
@Configuration
public class SingletonBeanRegister implements BeanFactoryAware, InitializingBean {

    private ConfigurableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        beanFactory.registerSingleton("myBean3", new MyBean("MyBean3"));
        beanFactory.registerSingleton("myBean4", new MyBean("MyBean4"));
    }
}

普通方式1和普通方式2还可以加载properties,即通过@Autowired引入定义的Properties属性,但是继承BeanDefinitionRegistryPostProcessor的方式不可以,因为postProcessBeanDefinitionRegistry方法会更早执行,更具体的原因目前还没有一个更深入的探究,先记一下。
BeanDefinitionBuilder还有设置属性的方法,一般使用以下两种

//这一种通过构造函数依次将属性set进去
beanDefinitionBuilder.addConstructorArgValue("fieldValue");
//这一种是set某个具体的属性
beanDefinitionBuilder.addPropertyValue("fieldName", "fieldValue");
四、测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanDefinitionApplicationTests {

    @Autowired
    NormalBean1 normalBean1;
    @Autowired
    NormalBean2 normalBean2;
    @Autowired
    NormalBean3 normalBean3;
    @Autowired
    ServiceBean1 serviceBean1;
    @Autowired
    ServiceBean2 serviceBean2;

    @Test
    public void test() {
        normalBean1.print();
        normalBean2.print();
        normalBean3.print();
        serviceBean1.print();
        serviceBean2.print();
    }
}

测试结果:
Bean实例化

ServiceClass1()
ServiceClass2()
NormalClass1()
NormalClass2()
NormalClass3()
//这些是控制台打印的实例化Bean的语句(二中的Bean里,构造函数会打印这些信息)

调用结果

in NormalBean1.print()
in NormalBean2.print()
调用NormalBean1.print()
in NormalBean1.print()
in NormalBean3.print()
调用ServiceBean1.print()
in ServiceBean1.print()
in ServiceBean1.print()
in ServiceBean2.print()
调用NormalBean1.print()
in NormalBean1.print()
五、总结

在实际使用过程中,Spring启动时扫描自定义注解,是通过BeanFactoryPostProcessor接口的postProcessBeanFactory方法,configurableListableBeanFactory.getBeansWithAnnotation(AutoDiscoverClass.class);获取每一个有自定义注解的Bean。这种方式,不适合当存在一些特殊的需求。而我们通过动态注册Bean可以满足我们特定的需求。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值