使用Spring容器动态注册和获取Bean

有时候需要在运行时动态注册Bean到Spring容器,并根据名称获取注册的Bean。比如我们自己的SAAS架构的系统需要调用ThingsBoard API和Thingsboard交互,就可以通过ThingsBoard提供的RestClient工具类。但这要求每个租户使用自己唯一的RestClient,为了达到此目的,系统启动时需要将每个租户的RestClient加载到Spring容器中以供租户随时使用,另外系统管理员可以在系统中随时创建新的租户,因此就需要在系统启动后运行过程随时可以注册新的RestClient到Spring容器中。

下面从运行时手动注册Bean到Spring容器以及从Spring容器中获取容器管理的Bean入手进行介绍。

  1. 运行时注册Bean到Spring容器

访问接口

/**
     * 注册bean到Spring容器。使用构造函数参数初始化bean。
     * 备注:需要有默认构造器,即需要有无参构造器。
     * @param beanName
     * @param clazz
     * @param constructorArgs
     */
    public static void registerBean(String beanName, Class<?> clazz, Object... constructorArgs) {
        registerBean(beanName, clazz, new InitBean() {
            @Override
            public void init(BeanDefinitionBuilder beanDefinitionBuilder) {
                log.info("使用构造函数参数初始化class[{}]",clazz);
                if(constructorArgs!=null&&constructorArgs.length>0){
                    for (Object constructorArg : constructorArgs) {
                        beanDefinitionBuilder.addConstructorArgValue(constructorArg);
                    }
                }
            }
        });
    }


    /**
     * 注册bean到spring容器中。使用属性参数初始化bean。
     * @param beanName 名称
     * @param clazz    class
     */
    public static void registerBean(String beanName, Class<?> clazz, Map<String, Object> propertyValueMap) {
       registerBean(beanName, clazz, new InitBean() {
           @Override
           public void init(BeanDefinitionBuilder beanDefinitionBuilder) {
               log.info("使用属性参数初始化class[{}]",clazz);
               if(propertyValueMap!=null){
                   propertyValueMap.forEach((k,v)->{
                       beanDefinitionBuilder.addPropertyValue(k, v);
                   });
               }
           }
       });
    }

核心代码:

    private static void registerBean(String beanName, Class<?> clazz, InitBean initBean) {
        // 1. 检查是否存在重名的bean,如果存在打印警告日志,并且返回,
        if (defaultListableBeanFactory.containsBean(beanName)) {
            log.warn("The Bean  [{}] for  type [{}] is already exists. Please check.", beanName, clazz.getName());
            return;
        }
        // 2. 通过BeanDefinitionBuilder创建bean定义
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

        //3. 初始化Bean
        if (initBean != null) {
            initBean.init(beanDefinitionBuilder);
        }

        // 4. 注册bean
        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
        log.info("register bean [{}],Class [{}] success.", beanName, clazz);
    }

由于初始化Bean有2重方式,一种是设置Property的方式(必须有默认的构造函数),一种是构造函数的方式,为了避免重复的代码特写了回调类InitBean

   public interface InitBean{
        void init( BeanDefinitionBuilder beanDefinitionBuilder);
    }

ApplicationContext和DefaultListableBeanFactory的获取

@Slf4j
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext = null;
    private static DefaultListableBeanFactory defaultListableBeanFactory;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringContextUtil.applicationContext == null) {
            SpringContextUtil.applicationContext = applicationContext;
        }
        //将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        // 获取bean工厂并转换为DefaultListableBeanFactory
        this.defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
        log.info("init ApplicationContext and  BeanFactory Success.");
    }
    ....
  1. Bean从Spring容器中的动态获取

提供三种方式从Spring 容器中获取bean,分别是根据bean的名称,bean的class类型(bean是gingleton的)根据bean的名称以及class类型。

    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

如上,使用如上介绍的注册和获取Bean的方式就可以轻松获得,运行时动态注册和获取Bean的能力。

备注:在SpringBoot微服务启动时手动完成Bean的注册可以利用SpringBoot的提供的org.springframework.CommandLineRunner或者org.springframework.bootApplicationRunner`

参考示例:

@Component
@Slf4j
public class TenantRestClientInit implements CommandLineRunner {
 

    @Override
    public void run(String... args) throws Exception {
        initSomeThings();
    }

    private void initSomeThings(){
       		....
        }

    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值