Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

前言


 

  很久没有写博客了,也是两个原因:一是自己觉得一直在班门弄斧,其实自己没什么技术可言;二是很多朋友的问题实际上可以自行解决,我经常觉得不该我来过问,或者是有时候我认为技术还得靠自己钻研,我一两句话不能让各位醍醐灌顶……

  不过还是偶尔做一下记录,免得博客界渐渐就把我淡忘了。

  这篇博客主要给大家讲一下Spring中如何批量修改Bean的定义。

应用场景

  我之前遇到一个很有意思的问题:我需要批量重定义特定类型的由Spring容器托管的Bean。具体体现在,我有很多控制器类(Controller)和校验器类(Validator),我希望他们都是多例(Prototype)的,而Spring默认创建实例是单例(Singleton)的。有朋友可能要问:为什么不自己在Bean定义时加参数呢@Scope("prototype")?我的回答很简单:懒……。因为我的Bean声明是这样的:

  Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

  Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

  那么我就没法很精确得去设置控制器和校验器的类实例为多例,因为我这里很笼统。

  接下来我们详解怎样使用代码实现设置特定Bean定义的修改。

ApplicationListener-ContextRefreshedEvent

  我们可以监听一个Spring的ApplicationContext的事件来让Spring的Bean容器配置完成后通知我们来处理一下。

<bean id="beanDefineConfigue" class="com.xx.yy.zz.BeanDefineConfigue"></bean>
1 public class BeanDefineConfigue implements ApplicationListener<ContextRefreshedEvent> {
2     
3     @Override
4     public void onApplicationEvent(ContextRefreshedEvent event) {
5         
6     }
7 }

  ContextRefreshedEvent是“Event raised when an ApplicationContext gets initialized or refreshed.(当ApplicationContext初始化完成或刷新完成后产生的事件)”

  当然,我们可以在onApplicationEvent函数内“搞事儿”了!

BeanFactory-BeanDefinition-registerBeanDefinition

 

 1 public void onApplicationEvent(ContextRefreshedEvent event) {
 2         ConfigurableApplicationContext context = (ConfigurableApplicationContext) event.getApplicationContext();
 3         DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();
 4         // 控制器
 5         String[] controllers = factory.getBeanNamesForAnnotation(Controller.class);
 6         if(controllers != null) {
 7             for(String controllerBeanName : controllers) {
 8                 BeanDefinition beanDefine = factory.getBeanDefinition(controllerBeanName);
 9                 String scope = beanDefine.getScope();
10                 if(scope == null || !scope.equals(ConfigurableBeanFactory.SCOPE_PROTOTYPE)) {
11                     beanDefine.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE);
12                     factory.registerBeanDefinition(controllerBeanName, beanDefine);
13                 }
14             }
15         }
16         // 校验器
17         Object[] validators = factory.getBeanNamesForType(Validator.class);
18         if(validators != null) {
19             for(Object _validatorBeanName : validators) {
20                 String validatorBeanName = String.valueOf(_validatorBeanName);
21                 BeanDefinition beanDefine = factory.getBeanDefinition(validatorBeanName);
22                 String scope = beanDefine.getScope();
23                 if(scope == null || !scope.equals(ConfigurableBeanFactory.SCOPE_PROTOTYPE)) {
24                     beanDefine.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE);
25                     factory.registerBeanDefinition(validatorBeanName, beanDefine);
26                 }
27             }
28         }
29     }

  可以看到,核心代码其实很少,也很容易懂!我针对控制器类和校验器类的所有Bean定义(使用getBeanNamesForType函数可以获取给定类型及其子类型的所有Bean定义;上文对Controller类型的检测是使用了Spring的@Controller,这是因为我个人的业务需求不一样,大家注意,beanfactory中的各种方法大家查看API灵活使用),检测到它们scope不为prototype时强制重设!

说在结尾


  我一直跟很多向我咨询Spring运用的朋友们说起:先把Spring看成一个Hashtable,它存了很多键值,就是Bean定义(包括Bean关系等等);其次是Spring不会凭空产生,更不会凭空为你托管对象,我们使用Spring的方式最终都是{new XXYYZZApplicationContext().getBean(XXYYZZ)},你在web.xml中定义的ContextLoaderListener,或者是其他中间件(Struts等)。

  “万事万物都有其源头。”所以,如果观看此篇博文的朋友进行单元测试时发现自动注入等功能未实现,请看看你是否为Spring容器创建了对象。

联系我,一起交流


 

 欢迎您移步我们的交流群,无聊的时候大家一起打发时间:Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

 

 Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

或者通过QQ与我联系:Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

Spring进阶教程之在ApplicationContext初始化完成后重定义Bean

 (最后编辑时间2015-12-31 16:22:53)

 

;