之前遇到一個很有意思的問題:我需要批量重定義特定類型的由Spring容器托管的Bean。具體體現在,我有很多控制器類(Controller)和校驗器類(Validator),我希望他們都是多例(Prototype)的,而Spring默認創建實例是單例(Singleton)的。有朋友可能要問:為什么不自己在Bean定義時加參數呢@Scope("prototype")?我的回答很簡單:懶……。因為我的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看成一個Hashtable,它存了很多鍵值,就是Bean定義(包括Bean關系等等);其次是Spring不會憑空產生,更不會憑空為你托管對象,我們使用Spring的方式最終都是{new XXYYZZApplicationContext().getBean(XXYYZZ)},你在web.xml中定義的ContextLoaderListener,或者是其他中間件(Struts等)。
“萬事萬物都有其源頭。”所以,如果觀看此篇博文的朋友進行單元測試時發現自動注入等功能未實現,請看看你是否為Spring容器創建了對象。

