spring boot 动态注入bean
方法一
SpringContextUtil
public class SpringContextUtil {
private static ApplicationContext applicationContext;
//获取上下文
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//设置上下文
public static void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}
//通过名字获取上下文中的bean
public static Object getBean(String name){
return applicationContext.getBean(name);
}
//通过类型获取上下文中的bean
public static Object getBean(Class<?> requiredType){
return applicationContext.getBean(requiredType);
}
}
启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext app = SpringApplication.run(Application.class, args);
SpringContextUtil.setApplicationContext(app);
}
}
测试bean
@Component
public class TestService {
public String doService(String contxt){
System.err.printf(contxt+"hello service");
return "hello service";
}
}
//无注入
public class TestController implements InitializingBean {
@Autowired
private TestService testService;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("我是动态注册的你,不是容器启动的时候注册的你");
}
public String toAction(String content){
return "-->" + testService.doService(content);
}
}
测试
@RestController
public class CallCSBController {
@GetMapping("/bean")
public String registerBean() {
//将applicationContext转换为ConfigurableApplicationContext
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) SpringContextUtil.getApplicationContext();
// 获取bean工厂并转换为DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
// 通过BeanDefinitionBuilder创建bean定义
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestController.class);
// 设置属性userService,此属性引用已经定义的bean:userService,这里userService已经被spring容器管理了.
// beanDefinitionBuilder.addPropertyReference("testService", "testService");
// 注册bean
defaultListableBeanFactory.registerBeanDefinition("testController", beanDefinitionBuilder.getRawBeanDefinition());
TestController userController = (TestController) SpringContextUtil.getBean("testController");
return userController.toAction("动态注册生成调用");
//删除bean.
//defaultListableBeanFactory.removeBeanDefinition("testService");
}
}
以上参考
链接:https://www.jianshu.com/p/41c716e7c31b
方法二(略有不同)
工具类
package com.theeternity.beans.applicationContextRegister;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @program: apiboot
* @description: 获取ApplicationContext, 实现动态注入bean
* @author: TheEternity Zhang
* @create: 2019-06-22 12:15
*/
@Component
@Slf4j
public class ApplicationContextRegister implements ApplicationContextAware {
private static ApplicationContext APPLICATION_CONTEXT;
/**
* 设置spring上下文
* @param applicationContext spring上下文
* @throws BeansException
* */
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.debug("ApplicationContext registed-->{}", applicationContext);
APPLICATION_CONTEXT = applicationContext;
}
/**
* 获取容器
* @return
*/
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
/**
* 获取容器对象
* @param type
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> type) {
return APPLICATION_CONTEXT.getBean(type);
}
public static <T> T getBean(String name,Class<T> clazz){
return APPLICATION_CONTEXT.getBean(name, clazz);
}
public static Object getBean(String name){
return APPLICATION_CONTEXT.getBean(name);
}
}
测试bean
@Component
public class TestService {
public String doService(String contxt){
System.err.printf(contxt+"hello service");
return "hello service";
}
}
//无注入
public class TestController implements InitializingBean {
@Autowired
private TestService testService;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("我是动态注册的你,不是容器启动的时候注册的你");
}
public String toAction(String content){
return "-->" + testService.doService(content);
}
}
测试
@RestController
public class CallCSBController {
@GetMapping("/bean")
public String registerBean() {
//将applicationContext转换为ConfigurableApplicationContext
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) ApplicationContextRegister.getApplicationContext();
// 获取bean工厂并转换为DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
// 通过BeanDefinitionBuilder创建bean定义
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestController.class);
// 设置属性userService,此属性引用已经定义的bean:userService,这里userService已经被spring容器管理了.
// beanDefinitionBuilder.addPropertyReference("testService", "testService");
// 注册bean
defaultListableBeanFactory.registerBeanDefinition("testController", beanDefinitionBuilder.getRawBeanDefinition());
TestController userController = (TestController) ApplicationContextRegister.getBean("testController");
return userController.toAction("动态注册生成调用");
//删除bean.
//defaultListableBeanFactory.removeBeanDefinition("testService");
}
第一种方法的另外一种形式
/**
//获取ApplicationContext
ApplicationContext ctx=ApplicationContextRegister.getApplicationContext();
//获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
//创建bean信息.
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class);
beanDefinitionBuilder.addPropertyValue("name","张三");
//动态注册bean.
defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition());
//获取动态注册的bean.
TestService testService =ctx.getBean(TestService.class);、testService.print();
*/
@GetMapping("/bean2")
public String registerBean2() {
TestController userController = (TestController) ApplicationContextRegister.getBean(TestController.class);
return userController.toAction("动态注册生成调用");
}
}
以上参考:
主力:https://www.jianshu.com/p/41c716e7c31b
辅助:https://www.jb51.net/article/140157.htm
拓展理解
我们通过getBean来获得对象,但这些对象都是事先定义好的,我们有时候要在程序中动态的加入对象.因为如果采用配置文件或者注解,我们要加入对象的话,还要重启服务,如果我们想要避免这一情况就得采用动态处理bean,包括:动态注入,动态删除。
本节大纲 :
(1)动态注入bean思路;
(2)动态注入实现代码;
(3)多次注入同一个bean的情况;
(4)动态删除;
接下来我们看下具体的内容:
(1)动态注入bean思路;
在具体进行代码实现的时候,我们要知道,Spring管理bean的对象是BeanFactory,具体的是DefaultListableBeanFactory,在这个类当中有一个注入bean的方法:registerBeanDefinition,在调用registerBeanDefinition方法时,需要BeanDefinition参数,那么这个参数怎么获取呢?Spring提供了BeanDefinitionBuilder可以构建一个BeanDefinition,那么我们的问题就是如何获取BeanFactory了,这个就很简单了,只要获取到ApplicationContext对象即可获取到BeanFacory了。
(2)动态注入实现代码;
综上所述,如果我们要编写一个简单里的例子的话,那么分以个几个步骤进行编码即可进行动态注入了:
<1>. 获取ApplicationContext;
<2>. 通过ApplicationContext获取到BeanFacotory;
<3>. 通过BeanDefinitionBuilder构建BeanDefiniton;
<4>. 调用beanFactory的registerBeanDefinition注入beanDefinition;
<5>. 使用ApplicationContext.getBean获取bean进行测试;
很明显我们需要先定义个类进行测试,比如TestService代码如下:
package com.kfit.demo.service;
public class TestService {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void print(){
System.out.println("动态载入bean,name="+name);
}
}
注意:这里没有使用@Service和配置文件进行注入TestService。
那么下面我们的目标就是动态注入TestService了,根据以上的分析,我们进行编码,具体代码如下:
//获取context. -- Angel -守护天使
ApplicationContext ctx = (ApplicationContext) SpringApplication.run(App.class, args);
//获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
//创建bean信息.
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class);
beanDefinitionBuilder.addPropertyValue("name","张三");
//动态注册bean.
defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition());
//获取动态注册的bean.
TestService testService =ctx.getBean(TestService.class);
testService.print();
执行代码我们会在控制台看到如下打印信息:
动态载入bean,name=张三
到这里,就证明我们的代码很成功了。
(3)多次注入同一个bean的情况;
多次注入同一个bean的,如果beanName不一样的话,那么会产生两个Bean;如果beanName一样的话,后面注入的会覆盖前面的。
第一种情况:beanName一样的代码:
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class);
defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition());
TestService testService =ctx.getBean(TestService.class);
testService.print();
运行看控制台: 动态载入bean,name=李四
第二种情况:beanName不一样的代码:
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class);
defaultListableBeanFactory.registerBeanDefinition("testService1", beanDefinitionBuilder.getBeanDefinition());
TestService testService =ctx.getBean(TestService.class);
testService.print();
此时如果没有更改别的代码直接运行的话,是会报如下错误的:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.kfit.demo.service.TestService] is defined: expected single matching bean but found 2: testService1,testService
大体意思就是在getBean的时候,找到了两个bean,这时候就不知道要获取哪个了,所以在获取的时候,我们就要指定我们是要获取的testService还是testService1,只需要修改一句代码:
将代码:
TestService testService =ctx.getBean(TestService.class);
修改为:
TestService testService =ctx.getBean("testService");
(4)动态删除;
相对于动态注入,动态删除就很简单了,直接奉上代码:
//删除bean.
defaultListableBeanFactory.removeBeanDefinition("testService");
拓展参考:
https://412887952-qq-com.iteye.com/blog/2348445
spring boot 动态注入bean的更多相关文章
- Spring Boot动态注入删除bean
Spring Boot动态注入删除bean 概述 因为如果采用配置文件或者注解,我们要加入对象的话,还要重启服务,如果我们想要避免这一情况就得采用动态处理bean,包括:动态注入,动态删除. 动态注入 ...
- Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean
在阅读Spring Boot源码时,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入.它是Spring中一个强大的扩展接口.本篇文 ...
- 基于ImportBeanDefinitionRegistrar和FactoryBean动态注入Bean到Spring容器中
基于ImportBeanDefinitionRegistrar和FactoryBean动态注入Bean到Spring容器中 一.背景 二.实现方案 1.基于@ComponentScan注解实现 2.基 ...
- 70.打印所有Spring boot载入的bean【从零开始学Spring Boot】
[从零开始学习Spirng Boot-常见异常汇总] 问题的提出: 我们在开发过程当中,我们可能会碰到这样的问题:No qualifying bean 就是我们定义的bean无法进行注入,那到底是什 ...
- spring boot 配置注入
spring boot配置注入有变量方式和类方式(参见:<spring boot 自定义配置属性的各种方式>),变量中又要注意静态变量的注入(参见:spring boot 给静态变量注入值 ...
- springBoot 动态注入bean(bean的注入时机)
springBoot 动态注入bean(bean的注入时机) 参考博客:https://blog.csdn.net/xcy1193068639/article/details/81517456
- 关于spring boot自动注入出现Consider defining a bean of type 'xxx' in your configuration问题解决方案
搭建完spring boot的demo后自然要实现自动注入来体现spring ioc的便利了,但是我在实施过程中出现了这么一个问题,见下面,这里找到解决办法记录下来,供遇到同样的问题的同僚参考 Des ...
- Spring中如何动态注入Bean实例教程
前言 在Spring中提供了非常多的方式注入实例,但是由于在初始化顺序的不同,基于标注的注入方式,容易出现未被正确注入成功的情况. 本文将介绍一种在实际项目中基于动态的方式来提取Spring管理的Be ...
- spring boot: 一般注入说明(五) @Component, application event事件为Bean与Bean之间通信提供了支持
spring的事件,为Bean与Bean之间通信提供了支持,当一个Bean处理完成之后,希望另一个Bean知道后做相应的事情,这时我们就让另外一个Bean监听当前Bean所发送的事件. spring的 ...
随机推荐
- mysql学习笔记(四):group by,limit,to_days(),from_days()
1. [Err] 1055 - Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated co ...
- CRPR/CPPR
S CRPR clock reconvergence pessimism removal C CPPR clock path pessimism removal 剔除公共clock path上的悲 ...
- Java中正负数的存储方式-正码 反码和补码
Java中正负数的存储方式-正码 反码和补码 正码 我们以int 为例,一个int占用4个byte,32bits 0 存在内存上为 00000000 00000000 00000000 0000000 ...
- python中 使用join()方法
使用join()方法 对各种数据类型中元的素进行合并拼接 "连接符".join(列表或元组或字符串或字典) 返回的是一个使用连接符进行拼接的字符串 如果对象是列表,元组,就是以一个 ...
- 试题编号: 201903-3 试题名称: 损坏的RAID5
这题的数据未免也太水了,题目的意思好像默认是每块磁盘装载数据的长度是相等的.我写了判断每次取数据是否会超过每块磁盘存的数据的长度,然而并没有什么卵用.交上去20分,写了个数据测了下,如果要求的块太大的 ...
- 201771010135 杨蓉庆《面对对象程序设计(java)》第十七周学习总结
1.实验目的与要求 (1) 掌握线程同步的概念及实现技术: (2) 线程综合编程练习 一.理论知识 ⚫ 线程同步 (1)多线程并发运行不确定性问题解决方案:引入线 程同步机制,使得另一线程要使用该方法 ...
- 基于Ubuntu和基于Debian的Linux Mint 20新特性一览
导读 Linux Mint 20 将基于 Ubuntu 20.04 LTS,同时,其 LMDE(Linux Mint Debian Edition,Debian 版本)4 也将到来,LMDE 版本基于 ...
- PL2303HXA自2012已停产
解决"PL2303HXA自2012已停产,请联系供货商" USB不识别的问题: 问题是WIN10自带的驱动不兼容引起的,只能安装早期驱动,再驱动程序中选择旧版本驱动即可
- Ansible playbooks(任务、角色、模板、变色器、)
playbooks配置文件: [root@ansible ~]# vim /etc/ansible/hosts [test01] 192.168.200.114 [test02] 192.168.20 ...
- jQuery之绑定焦点事件(焦点事件失效)
在使用jQuery绑定事件时,若某个事件不存在,则该事件后 绑定的事件均失效: 如图所示,若失去焦点事件checkEmail不存在, 会导致后面的事件checkMobile,和事件checkBirth ...