使用Spring容器动态注册和获取Bean
有时候需要在运行时动态注册Bean到Spring容器,并根据名称获取注册的Bean。比如我们自己的SAAS架构的系统需要调用ThingsBoard API和Thingsboard交互,就可以通过ThingsBoard提供的RestClient工具类。但这要求每个租户使用自己唯一的RestClient,为了达到此目的,系统启动时需要将每个租户的RestClient加载到Spring容器中以供租户随时使用,另外系统管理员可以在系统中随时创建新的租户,因此就需要在系统启动后运行过程随时可以注册新的RestClient到Spring容器中。
下面从运行时手动注册Bean到Spring容器以及从Spring容器中获取容器管理的Bean入手进行介绍。
- 运行时注册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.");
}
....
- 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(){
....
}
}
}
使用Spring容器动态注册和获取Bean的更多相关文章
- 【String注解驱动开发】如何按照条件向Spring容器中注册bean?这次我懂了!!
写在前面 当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,不 ...
- 【String注解驱动开发】面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?
写在前面 在前面的文章中,我们知道可以通过多种方式向Spring容器中注册bean.可以使用@Configuration结合@Bean向Spring容器中注册bean:可以按照条件向Spring容器中 ...
- 46、[源码]-Spring容器创建-注册BeanPostProcessors
46.[源码]-Spring容器创建-注册BeanPostProcessors 6.registerBeanPostProcessors(beanFactory);注册BeanPostProcesso ...
- 向Spring容器中注册组件的方法汇总小结
1.通过xml定义 <bean class=""> <property name="" value=""></ ...
- 从spring容器中取出注入的bean
从spring容器中取出注入的bean 工具类,代码如下: package com.hyzn.fw.util; import org.springframework.beans.BeansExcept ...
- spring中IOC容器注册和获取bean的实例
spring中常用的功能主要的是ioc和aop,此处主要说明下,实例注册和使用的方法,此为学习后的笔记记录总结 1.使用xml文件配置 在idea中创建maven工程,然后创建实例Person,然后在 ...
- spring项目启动后,获取bean的方法总结
如果在web项目中,用到定时器的朋友可能会遇到使用spring注解的方式获取bean的时候报空指针的异常.这是就可以使用手工的方法获取spring容器中的bean了. 下面是具体的方法: 1.先说一个 ...
- 将Spring容器跟随系统启动并获取容器对象
将Spring容器随系统启动的方法: 在web.xml中配置监听器,监听的对象为ContextLoaderListener <listener> <listener-class> ...
- Spring通过注解@Autowired/@Resource获取bean实例时为什么可以直接获取接口而不是注入的类
问: 这个问题困扰了我好久,一直疑问这个接口的bean是怎么注入进去的?因为只看到使用@Service注入了实现类serviceImpl,使用时怎么却获取的接口,而且还能调用到实现类的方法,难道这个接 ...
随机推荐
- linux more 命令的基础实现。
more :分页显示文件的内容 输入 more filename 会显示文件第一屏的内容,而且会有反白的 more 提升文件的百分比.空格=下一页,q=推出,h=连接帮助 more 的用法 1. mo ...
- 遇到奇葩的现象,给input的id为10的value属性赋值为6,但是怎么显示的时候值还是原先的9的
遇到奇葩的现象,给input的id为10的value属性赋值为6,但是怎么显示的时候值还是原先的9的 后来发现原来是id标签重复了,所有以后得注意了. 人员信息input的id属性与隐藏input属性 ...
- ACwing02.01背包问题
有\(N\)件物品和一个容量是\(V\)的背包.每件物品只能使用一次. 第\(i\)件物品的体积是\(v_i\),价值是\(w_i\). 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且 ...
- centos使用docker 安装 rabbitMq 消息队列
1.拉取镜像 docker pull rabbitmq:3-management 如果出现报错: Get https://registry-1.docker.io/v2/: net/http: req ...
- C++11之重写说明符override和final
关于 本文代码演示环境: win10 + vs2017 一个困扰 之前MFC用的多了,发现一个问题: 子类窗口的某个函数是否重载了基类的函数.解决办法是: 打开基类的代码,一个个排查. 这只是一个具体 ...
- 【LeetCode】680. Valid Palindrome II 验证回文字符串 Ⅱ(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 双指针 思路来源 初版方案 进阶方案 日期 题目地址 ...
- 【LeetCode】458. Poor Pigs 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- A. Toda 2
A. Toda 2 time limit per test 2 seconds memory limit per test 512 megabytes input standard input out ...
- 第四个知识点 P类复杂问题
第四个知识点 P类复杂问题 原文地址:http://bristolcrypto.blogspot.com/2014/10/52-things-number-4-complexity-class-p.h ...
- Chapter 4 Effect Modification
目录 4.1 Definition of effect modification 4.2 Stratification to identify effect modification 4.3 Why ...