使用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,使用时怎么却获取的接口,而且还能调用到实现类的方法,难道这个接 ...
随机推荐
- RocketMQ源码详解 | Broker篇 · 其五:高可用之主从架构
概述 对于一个消息中间件来讲,高可用功能是极其重要的,RocketMQ 当然也具有其对应的高可用方案. 在 RocketMQ 中,有主从架构和 Dledger 两种高可用方案: 第一种通过主 Brok ...
- 巧用符号链接迁移 HDFS 数据,业务完全无感知!
问题 JuiceFS 是一个基于对象存储的分布式文件系统,在之前跟对象存储比较的文章中已经介绍了 JuiceFS 能够保证数据的强一致性和极高的读写性能,因此完全可以用来替代 HDFS.但是数据平台整 ...
- 七层LB-NGINX 客户端获取协议Proxy Protocol介绍
协议诞生背景 在Web分布式系统中,一般会搭建复杂的load blance系统来提供高性能的web服务. 7层的SLB 有基于nginx/淘宝变种tengine的.后端RS会无法获取真实客户端IP. ...
- centos使用docker 安装 rabbitMq 消息队列
1.拉取镜像 docker pull rabbitmq:3-management 如果出现报错: Get https://registry-1.docker.io/v2/: net/http: req ...
- JAVA获取指定日期的周一的日期
/** * 获取当前周的周一的日期 * @param date 传入当前日期 * @return */ public static Date getThisWeekMonday(Date date) ...
- vue-子组件创建/注册/使用流程
流程分为三步 非单文件组件:(实际不用,因为很麻烦,框架都是多文件组件) 局部注册 1.创建一个组件 const school = Vue.extend({ // 传入配置对象 // 子组件配置对象不 ...
- 【LeetCode】102. Binary Tree Level Order Traversal 二叉树的层序遍历 (Python&C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS DFS 日期 题目地址:https://lee ...
- RotateRect(旋转矩形)的倾斜旋转变换矫正
在Opencv中的图像处理中,经常要用到minAreaRect()函数求最小外接矩形,该函数的返回值就是一个RotatedRect类对象. RotatedRect类定义如下: class CV_EXP ...
- <数据结构>关键路径
目录 AOV网和AOE网 AOV网 AOE网 定义 与AOV网的转化 AOE网中着重解决的两个问题 1.最长路径问题 2.关键活动问题 总结 最长路径 无正环的图 有向无环图的最短路径 其他情况 关键 ...
- 【jvm】04-我偷偷改了你编译后的class文件
[jvm]04-我偷偷改了你编译后的class文件 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮助到你的话请 ...