Spring 源碼分析:ApplicationContext
Spring 源碼分析:ApplicationContext
關鍵詞: Spring ApplicationCont分析ApplicationContext
Spring的bean包支持通過編碼方式管理和操作bean的基本功能,ApplicationContext
則以Framework的方式提供BeanFactory的所有功能。使用ApplicationContext,你可以讓
系統加載你的bean(例如,在Servlet容器初始化ContextLoaderServlet時,通過
ContextLoader類加載Spring Framework),而不是使用編碼方式來加載。
ApplicationContext接口是context包的基礎,位于
org.springframework.context包里,提供了BeanFactory的所有功能。除此之外,
ApplicationContext為了支持Framework的工作方式,提供了以下的功能:
l.MessageSource,提供了語言信息的國際化支持 2.提供資源(如URL和文件系統)的訪問支持 3.為實現了ApplicationListener接口的bean提供了事件傳播支持 4.為不同的應用環境提供不同的context,例如支持web應用的XmlWebApplicationContext類 下面的源代碼分析主要集中在ApplicationContext接口特有的功能上,如國際化支持,
資源訪問和bean的事件傳播。 我的問題
現在我的問題是,ApplicationContext是如何實現上面提到的功能的?下面的分析
將作出回答。 準備測試用例
1. 首先在類路徑根目錄編寫測試國際化支持的testmsg.xml,并將它加入Spring IDE
的管理范圍:
<beans> <bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>message</value> </list> </property> </bean> </beans> 2. 編寫測試用例,測試國際化支持和資源訪問的功能。 public class MsgTest extends TestCase { ApplicationContext ctx = null; public MsgTest(String arg0) { super(arg0); } protected void setUp() throws Exception { super.setUp(); ctx = new FileSystemXmlApplicationContext("testmsg.xml"); } public void testMessageResource() { Object[] args = {"我", "你"}; String msg = ctx.getMessage("hello", args, Locale.CHINA); //System.out.println("msg=" + msg); assertEquals("我和你", msg); Resource rs = ctx.getResource("classpath:log4j.properties"); assertTrue(rs.exists()); } } 3. 在類路徑根目錄創建屬性文件message.properties,內容為hello={0}和{1}。 此時運行TestCase,果然不出所料,Junit視圖的測試狀態是紅條。將打印msg變量的
語句打開,重新測試,發現"和"字是亂碼。
在message.properties文件中將"和"字改為ASCII碼\u548c,重新運行TestCase,
綠條,測試通過。
將testmsg.xml中bean的id改為messageSource1,重新運行測試,出現紅條,
測試失敗,說明bean的名稱必須是messageSource,這點值得注意。至于其中的原因稍后
說明。 ApplicationContext接口通過繼承BeanFactory,MessageSource和ResourceLoader
三個接口,分別支持管理和操作bean的功能,語言信息的國際化支持以及對資源訪問的支持。
AbstractApplicationContect是ApplicationContext的抽象實現類,它的繼承層次較
為紊亂,我覺得這里應該進行代碼重構。AbstractXmlApplicationContext是
AbstractApplicationContext的子類,提供了對XML配置文件的支持,它有三個子類,分別
用于不同的應用環境。
對于MessageSource,Spring提供了兩個bean實現,ResourceBundleMessageSource和
ReloadableResourceBundleMessageSource。前者提供了訪問Properties文件的支持,后者
添加了無需重啟JVM,重新加載Properties文件的支持。
ApplicationContext的國際化和資源訪問支持
1. 如類層次圖所示,在我們的例子中,FileSystemXmlApplicationContext使用
DefaultListableBeanFactory裝載和解釋testmsg.xml配置文件(參見代碼分析的
BeanFactory部分)。 2. FileSystemXmlApplicationContext根據配置文件的BeanDefinition創建
ResourceBundleMessageSource,加載<list>元素定義的Properties文件,并保存在
AbstractApplicationContext的屬性中。當客戶程序調用getMessage方法時,
AbstractApplicationContext調用ResourceBundleMessageSource的getMessage方法返回
Message信息。
3. 至于上節提到的MessageSource的id只能是messageSource,是因為
AbstractApplicationContext的initMessageSource()方法中,有這樣一段代碼:
this.messageSource = (MessageSource) getBean(MESSAGE_SOURCE_BEAN_NAME);
其中MESSAGE_SOURCE_BEAN_NAME的定義為:
static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
原因找到了,其實只要稍做代碼重構,即可消除這個缺陷。
4. 如類層次圖所示,AbstractApplicationContext繼承了DefaultResourceLoader,
當客戶程序調用getResource方法時,使用父類中實現的方法來處理。
ApplicationContext的事件傳播
準備測試用例
1. 首先編寫測試用例。 public class SenderBeanTest extends TestCase { ApplicationContext ctx = null; protected void setUp() throws Exception { super.setUp(); ctx = new FileSystemXmlApplicationContext("testlistener.xml"); } public void testSendEmail() { SenderBean sender = (SenderBean)ctx.getBean("sender"); String msg = sender.sendMessage("test message"); assertEquals("test message", msg); } }
2. 接著編寫testlistener.xml配置文件。 <beans> <bean id="sender" class="unittest.SenderBean"/> <bean id="listener" class="unittest.MessageListener"/> </beans>
3. 最后編寫SenderBean,MessageListener和MessageEvent類。 public class SenderBean implements ApplicationContextAware { private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public String sendMessage(String msg) { MessageEvent event = new MessageEvent(msg); this.applicationContext.publishEvent(event); return msg; } } public class MessageListener implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { if (event instanceof MessageEvent) { System.out.println("I got the message:" + event.getSource()); } } } public class MessageEvent extends ApplicationEvent { public MessageEvent(Object source) { super(source); System.out.println(this.getTimestamp() + ":" + source); } }
運行測試案例SenderBeanTest,綠條,測試通過。Console窗口出現以下DEBUG
信息:
…… 796 DEBUG support.DefaultListableBeanFactory - Returning
cached instance of singleton bean 'sender' 1085553911796:test message 796 DEBUG support.FileSystemXmlApplicationContext - Publishing event
in context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=13549765]:
unittest.MessageEvent[source=test message] I got the message:test message 事件傳播的實現 1. FileSystemXmlApplicationContext的構造器調用AbstractApplicationContext的
refresh方法。如圖所示,refresh方法調用refreshListeners方法。
2. AbstractApplicationContext的refreshListeners方法使用BeanFactory的
getBeanOfType方法得到所有ApplicationListener類(本例中是MessageListener),并使用
addListener方法把它們都放入ApplicationEventMulticasterImpl的Set容器中
(eventListeners變量)。
3. 如圖所示,SenderBean實現ApplicationContextAware接口,并通過
setApplicationContext方法注入ApplicationContext對象實例。
4. 當調用SenderBean類sendMessage方法時,AbstractApplicationContext調用
publishEvent方法。
5. AbstractApplicationContext類的publishEvent方法調用
ApplicationEventMulticasterImpl類的onApplicationEvent方法。
6. ApplicationEventMulticasterImpl通知Set容器中所有的ApplicationListener
對象,調用它們的onApplicationEvent方法。
從以上的過程可以看出,ApplicationContext將事件通知所有的
ApplicationListener。如果ApplicationListener的子類(如MessageListener)只想接受
指定的事件類型,需要自己編寫過濾代碼,如例子中的if (event instanceof MessageEvent)。
posted on 2006-03-02 17:09 都市淘沙者 閱讀(1485) 評論(0) 編輯 收藏 所屬分類: Spring+Struts+Hibernate