有时候需要在运行时动态注册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(){
....
} }
}

最新文章

  1. 深入理解redis持久化
  2. htmlentities,html_entity_decode,addslashes
  3. 转: JAVA递归算法实例小结
  4. eclipse 导入工程报错Unable to execute dex: Multiple dex files define Landroid/annotation/SuppressLint
  5. Html5编辑工具
  6. Entity Framework Lambda 实现多列Group by,并汇总求和
  7. Fundamental Datastructure
  8. 【21】必须返回对象时,别妄想返回器reference
  9. Mac 系统下安装 MySql
  10. 通过DAC来连接SQL Server
  11. [Angular 2]ng-class and Encapsulated Component Style2
  12. hdu 5562 Clarke and food(贪心)
  13. Windows Phone APP中禁用截图
  14. 标签—box-shadow
  15. HTML/CSS/Javascript调试入门(转)
  16. dedecms二次开发
  17. SpringMVC框架四:异常处理器
  18. 第一册:lesson eighty three.
  19. 设计模式---数据结构模式之迭代器模式(Iterate)
  20. [转]git操作指南

热门文章

  1. Spring MVC环境搭建和配置
  2. 拖动条形图设置任务关联(Project)
  3. CF135A Replacement 题解
  4. LuoguP7369 [COCI2018-2019#4] Elder 题解
  5. SpringBoot整合logback日志框架
  6. c++之元组std::tuple常见用法
  7. 【LeetCode】922. Sort Array By Parity II 解题报告(Python)
  8. 【LeetCode】908. Smallest Range I 解题报告(Python)
  9. DevTools 实现原理与性能分析实战
  10. sping练习,在Eclipse搭建的Spring开发环境中,使用工厂方式创建Bean对象,将创建的Bean对象输出到控制台。