標籤:
之前遇到一個很有意思的問題:我需要批量重定義特定類型的由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 @Override4 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容器建立了對象。
Spring進階教程之在ApplicationContext初始化完成後重定義Bean