使用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,使用时怎么却获取的接口,而且还能调用到实现类的方法,难道这个接 ...
随机推荐
- M语言的写、改、删(Power Query 之 M 语言)
M语言基本上和其他语言一样,用敲键盘的方式写入.修改.删除,这个是废话. M语言可以在[编辑栏]或[高级编辑器]里直接写入.修改.删除,这个也是废话. M语言还有个地方可以写入.修改.删除,就是[自定 ...
- 『学了就忘』Linux系统管理 — 85、工作管理相关命令
目录 1.工作管理简介 2.如何把命令放入后台 3.后台管理命令 (1)查看后台的工作 (2)将后台暂停的工作恢复到前台执行 (3)把后台暂停的工作恢复到后台执行 4.后台命令脱离登录终端运行 1.工 ...
- 小迪安全 Web安全 基础入门 - 第一天 - 操作系统&名词&文件下载&反弹SHELL&防火墙绕过
一.专业名词 1.POC:(Proof of Concept),即概念验证.漏洞报告中的POC是一段说明或一个攻击的样例使读者能够确认这个漏洞是真实存在的. 2.EXP:exploit,即漏洞利用.对 ...
- CF1080B Margarite and the best present 题解
Content 有 \(t\) 次询问,每次询问给定两个整数 \(l,r\),求 \(\sum\limits_{i=l}^r (-1)^i\times i\). 数据范围:\(1\leqslant t ...
- CF177A1/A2 Good Matrix Elements 题解
Content 给定一个 \(n\times n\) 的矩阵,每个元素都有一个值,求出处于两条对角线以及中间行和中间列的元素之和. 数据范围:\(1\leqslant n\leqslant 5(\te ...
- 针对HttpClient 重写 HttpRequestRetryHandler针对特定异常 增加重试
调用方法: public static String doGet(String url) { try { RequestConfig defaultRequestConfig = RequestCon ...
- 前端实现list排序
需求 针对list中某个字段,实现list的升序和降序 效果图 代码 我是用在angular1.X中项目的,根据list中的sort字段进行排序. # sort.html <style> ...
- DKT模型及其TensorFlow实现(Deep knowledge tracing with Tensorflow)
今年2月15日,谷歌举办了首届TensorFlow Dev Summit,并且发布了TensorFlow 1.0 正式版. 3月18号,上海的谷歌开发者社区(GDG)组织了针对峰会的专场回顾活动.本文 ...
- JAVA直接连接Redis
引入maven <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</ ...
- Hibernate使用distinct返回不重复的数据,使用group by 进行分组
//distinct使用 public List<String> distinctDutyDate() { String hql="select distinct(dutyDat ...