Spring中的Bean除了前面提到的几种JavaConfig或者@Component等注解标识之外,也是可以动态的向Spring容器注册的,本篇博文将主要介绍
- 如何向Spring容器注册Bean
- 如何引用主动注册的Bean
- 注册的Bean中,如果依赖其他的Bean,怎么操作
I. 手动注册Bean方式
1. 核心实现类
以前也写过关于动态注册Bean的博文,如 180804-Spring之动态注册bean
我们的实现方式和上面也没什么区别,依然是借助BeanDefinition来创建Bean定义并注册到BeanFactory中,具体实现的核心代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class ManualRegistBeanUtil {
public static <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clazz, Object... args) { if(applicationContext.containsBean(name)) { Object bean = applicationContext.getBean(name); if (bean.getClass().isAssignableFrom(clazz)) { return (T) bean; } else { throw new RuntimeException("BeanName 重复 " + name); } }
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz); for (Object arg : args) { beanDefinitionBuilder.addConstructorArgValue(arg); } BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory(); beanFactory.registerBeanDefinition(name, beanDefinition); return applicationContext.getBean(name, clazz); } }
|
上面唯一的方法中,接收四个参数,源码中也有说明,稍微需要注意下的是Spring容器中不允许出现同名的Bean
2. 测试用例
动态创建Bean,并不是塞入容器之中就完结了,塞进去之后,是为了后续的使用,自然而然的就会有下面几种情形
a. 无其他Bean依赖
即不依赖其他的Bean, 单纯的供其他地方使用,这种情况下,主要需要测试的就是别人可以通过什么方式来使用它
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Slf4j public class ManualBean {
private int id;
public ManualBean() { Random random = new Random(); id = random.nextInt(100); }
public String print(String msg) { return "[ManualBean] print : " + msg + " id: " + id; } }
|
b. 依赖其他Bean
和前面一个不同,这个Bean内部需要注入其他的Bean,因此我们主动注册Bean时,能否将依赖的Bean也注入进去呢?
定义一个测试Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Slf4j public class ManualDIBean {
private int id;
@Autowired private OriginBean originBean;
private String name;
public ManualDIBean(String name) { Random random = new Random(); this.id = random.nextInt(100); this.name = name; }
public String print(String msg) { String o = originBean.print(" call by ManualDIBean! "); return "[ManualDIBean] print: " + msg + " id: " + id + " name: " + name + " originBean print:" + o; } }
|
其依赖的普通Bean定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Slf4j @Component public class OriginBean {
private LocalDateTime time;
public OriginBean() { time = LocalDateTime.now(); }
public String print(String msg) { return "[OriginBean] print msg: " + msg + ", time: " + time; } }
|
c. 普通Bean依赖主动注册的Bean
这个其实就是使用case了,主动注册的Bean也是被人使用的,那可以怎么使用呢?传统的Autowired可否?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Slf4j @Component public class AnoOriginBean { @Autowired private ManualBean manualBean;
public AnoOriginBean() { System.out.println("AnoOriginBean init: " + System.currentTimeMillis()); }
public String print() { return "[AnoOriginBean] print!!! manualBean == null ? " + (manualBean == null); } }
|
d. Bean注册实现
前面定义了两个需要手动注册的bean,所以就需要选择一个合适的地方来处理主动注册的逻辑,我们把这段逻辑放在AutoConfig中,用于测试演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Configuration public class BeanRegisterAutoConf {
public BeanRegisterAutoConf(ApplicationContext applicationContext) { System.out.println("BeanRegisterAutoConf init: " + System.currentTimeMillis()); registerManualBean((ConfigurableApplicationContext) applicationContext); }
private void registerManualBean(ConfigurableApplicationContext applicationContext) { ManualBean manualBean = ManualRegistBeanUtil.registerBean(applicationContext, "manualBean", ManualBean.class); manualBean.print("test print manualBean");
ManualDIBean manualDIBean = ManualRegistBeanUtil.registerBean(applicationContext, "manualDIBean", ManualDIBean.class, "依赖OriginBean的自定义Bean"); manualDIBean.print("test print manualDIBean"); } }
|
3. 实测演示
前面的测试case都准备好了,接着就需要实际的跑一下看看效果了,选择Rest服务来演示,创建一个简单的Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @RestController public class ShowController {
@Autowired private ManualBean manualBean; @Autowired private ManualDIBean manualDIBean; @Autowired private AnoOriginBean anoOriginBean;
public ShowController() { System.out.println("ShowController init: " + System.currentTimeMillis()); }
@GetMapping(path = "show") public String show(String msg) { Map<String, String> result = new HashMap<>(8); result.put("manualBean", manualBean == null ? "null" : manualBean.print(msg)); result.put("manualDIBean", manualDIBean == null ? "null" : manualDIBean.print(msg)); result.put("anoOriginBean",anoOriginBean.print()); return JSONObject.toJSONString(result); } }
|
上面就使用了三个Bean,两个主动注册的外加一个依赖了主动注册Bean的anoOriginBean (其实Controller本身也是一个使用主动注册Bean的Bean)
先预测一下结果:
- 如果 manualBean, manualDIBean 为空,表示不能直接通过
@Autowired 注解的方式引入手动注册的Bean;此时会抛npe
- 如果没有npe,且 AnoOriginBean内部依赖的manualBean也不是null,则表示直接用
@Autowired来注入没啥毛病(是否绝对呢?)
- manualDIBean 内部依赖了
originBean,也是通过注解方式注入,如果正常返回,表示手动注册的也可以这么引用其他的Bean;否则不行

执行结果如上图,简单来说,就是手动注册的Bean,和我们一般使用的Bean也没什么两样,原来可以怎么用,现在依然可以这么用
II. BeanDefinitionRegistryPostProcessor扩展方式
前面这种手动注入的方式有个不好的地方就是主动注册的这个逻辑,感觉写在什么地方都不太优雅,在Spring项目的源码中通过实现BeanDefinitionRegistryPostProcessor扩展方式接口的方式比较多,比如org.springframework.cloud.autoconfigure.RefreshAutoConfiguration
依葫芦画瓢实现一个
1. 实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Slf4j @Configuration public class AutoBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(AutoBean.class); BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); registry.registerBeanDefinition("autoBean", beanDefinition);
beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(AutoDIBean.class); beanDefinitionBuilder.addConstructorArgValue("自动注入依赖Bean"); beanDefinition = beanDefinitionBuilder.getBeanDefinition(); registry.registerBeanDefinition("autoDiBean", beanDefinition); }
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(AutoFacDIBean.class, () -> { AutoFacDIBean autoFacDIBean = new AutoFacDIBean("autoFac"); autoFacDIBean.setAutoBean(factory.getBean("autoBean", AutoBean.class)); autoFacDIBean.setOriginBean(factory.getBean("originBean", OriginBean.class)); return autoFacDIBean; }); BeanDefinition beanDefinition = builder.getRawBeanDefinition(); ((DefaultListableBeanFactory) factory).registerBeanDefinition("autoFacDIBean", beanDefinition); } }
|
接口的实现中,Bean的注册方式和前面的其实是一样的,这个接口提供了两个方法,通常实现第一个方法来做Bean的注册;两者从根本上也没太大的区别,上面只是给出了一种使用演示
2. 测试用例
测试的思路基本上和前面一样,定义了三个需要我们注册的Bean,一个没有外部依赖的AutoBean
1 2 3 4 5 6
| public class AutoBean {
public String print() { return "[AutoBean] " + System.currentTimeMillis(); } }
|
一个依赖外部Bean的AutoDIBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class AutoDIBean {
private String name;
@Autowired private OriginBean originBean;
public AutoDIBean(String name) { this.name = name; }
public String print() { return "[AutoDIBean] " + name + " originBean == null ? " + (originBean == null); }
}
|
一个用于主动创建和设置依赖的AutoFacDIBean (用于前面的实现类中的第二个方法的注册方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class AutoFacDIBean { private String name;
@Setter private OriginBean originBean;
@Setter private AutoBean autoBean;
public AutoFacDIBean(String name) { this.name = name; }
public String print() { return "[AutoDIBean] " + name + " originBean == null ? " + (originBean == null) + " | autoBean==null ? " + (autoBean == null); }
}
|
一个依赖了主动注册AutoBean的 AnoAutoOriginBean
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Component public class AnoAutoOriginBean { @Autowired private AutoBean autoBean;
public AnoAutoOriginBean() { System.out.println("AnoAutoOriginBean init: " + System.currentTimeMillis()); }
public String print() { return "[AnoAutoOriginBean] print!!! autoBean == null ? " + (autoBean == null); } }
|
3. 实测演示
同样写一个RestApi进行演示,通过实际的演示结果发现和前面没什么太大的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Autowired private AutoBean autoBean; @Autowired private AutoDIBean autoDIBean; @Autowired private AutoFacDIBean autoFacDIBean; @Autowired private AnoAutoOriginBean anoAutoOriginBean; @GetMapping(path = "auto") public String autoShow() { Map<String, String> result = new HashMap<>(8); result.put("autoBean", autoBean == null ? "null" : autoBean.print()); result.put("manualDIBean", autoDIBean == null ? "null" : autoDIBean.print()); result.put("autoFacDIBean",autoFacDIBean == null ? "null" : autoFacDIBean.print()); result.put("anoAutoOriginBean",anoAutoOriginBean.print()); return JSONObject.toJSONString(result); }
|

III. 其他
0. 相关
a. 文档
b. 源码
1. 一灰灰Blog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
3. 扫描关注
一灰灰blog

知识星球

打赏
如果觉得我的文章对您有帮助,请随意打赏。
微信打赏
支付宝打赏