Spring 源碼分析:ApplicationContext- -
??????????????????????????????????????
?
分析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類
?????? 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,這點值得注意。至于其中的原因稍后說明。
?????? 在message.properties文件中將"和"字改為ASCII碼\u548c,重新運行TestCase,綠條,測試通過。
?????? 將testmsg.xml中bean的id改為messageSource1,重新運行測試,出現紅條,測試失敗,說明bean的名稱必須是messageSource,這點值得注意。至于其中的原因稍后說明。
ApplicationContext類圖
?????? ApplicationContext接口相關的類圖如下,其中getParent和publishEvent方法分別支持分層的context和事件傳播功能。
?
?
??????
?????? 如以上的類繼承層次圖所示,ApplicationContext接口通過繼承BeanFactory,MessageSource和ResourceLoader三個接口,分別支持管理和操作bean的功能,語言信息的國際化支持以及對資源訪問的支持。
?????? AbstractApplicationContect是ApplicationContext的抽象實現類,它的繼承層次較為紊亂,我覺得這里應該進行代碼重構。AbstractXmlApplicationContext是AbstractApplicationContext的子類,提供了對XML配置文件的支持,它有三個子類,分別用于不同的應用環境。
?????? 對于MessageSource,Spring提供了兩個bean實現,ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。前者提供了訪問Properties文件的支持,后者添加了無需重啟JVM,重新加載Properties文件的支持。
?????? 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 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
???????????????? ?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
ApplicationContext有關事件的類圖
????????ApplicationContext事件傳播的類結構圖如下。
?
?
事件傳播的實現
?????? 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方法。
?????? 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)。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
