Spring 源碼分析:ApplicationContext
關(guān)鍵詞:
Spring ApplicationCont
分析ApplicationContext
Spring的bean包支持通過(guò)編碼方式管理和操作bean的基本功能,ApplicationContext
則以Framework的方式提供BeanFactory的所有功能。使用ApplicationContext,你可以讓
系統(tǒng)加載你的bean(例如,在Servlet容器初始化ContextLoaderServlet時(shí),通過(guò)
ContextLoader類加載Spring Framework),而不是使用編碼方式來(lái)加載。
ApplicationContext接口是context包的基礎(chǔ),位于
org.springframework.context包里,提供了BeanFactory的所有功能。除此之外,
ApplicationContext為了支持Framework的工作方式,提供了以下的功能:
l.MessageSource,提供了語(yǔ)言信息的國(guó)際化支持
2.提供資源(如URL和文件系統(tǒng))的訪問(wèn)支持
3.為實(shí)現(xiàn)了ApplicationListener接口的bean提供了事件傳播支持
4.為不同的應(yīng)用環(huán)境提供不同的context,例如支持web應(yīng)用的XmlWebApplicationContext類
下面的源代碼分析主要集中在ApplicationContext接口特有的功能上,如國(guó)際化支持,
資源訪問(wèn)和bean的事件傳播。
我的問(wèn)題
現(xiàn)在我的問(wèn)題是,ApplicationContext是如何實(shí)現(xiàn)上面提到的功能的?下面的分析
將作出回答。
準(zhǔn)備測(cè)試用例
1. 首先在類路徑根目錄編寫測(cè)試國(guó)際化支持的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. 編寫測(cè)試用例,測(cè)試國(guó)際化支持和資源訪問(wèn)的功能。
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. 在類路徑根目錄創(chuàng)建屬性文件message.properties,內(nèi)容為hello={0}和{1}。
此時(shí)運(yùn)行TestCase,果然不出所料,Junit視圖的測(cè)試狀態(tài)是紅條。將打印msg變量的
語(yǔ)句打開(kāi),重新測(cè)試,發(fā)現(xiàn)"和"字是亂碼。
在message.properties文件中將"和"字改為ASCII碼\u548c,重新運(yùn)行TestCase,
綠條,測(cè)試通過(guò)。
將testmsg.xml中bean的id改為messageSource1,重新運(yùn)行測(cè)試,出現(xiàn)紅條,
測(cè)試失敗,說(shuō)明bean的名稱必須是messageSource,這點(diǎn)值得注意。至于其中的原因稍后
說(shuō)明。
ApplicationContext接口通過(guò)繼承BeanFactory,MessageSource和ResourceLoader
三個(gè)接口,分別支持管理和操作bean的功能,語(yǔ)言信息的國(guó)際化支持以及對(duì)資源訪問(wèn)的支持。
AbstractApplicationContect是ApplicationContext的抽象實(shí)現(xiàn)類,它的繼承層次較
為紊亂,我覺(jué)得這里應(yīng)該進(jìn)行代碼重構(gòu)。AbstractXmlApplicationContext是
AbstractApplicationContext的子類,提供了對(duì)XML配置文件的支持,它有三個(gè)子類,分別
用于不同的應(yīng)用環(huán)境。
對(duì)于MessageSource,Spring提供了兩個(gè)bean實(shí)現(xiàn),ResourceBundleMessageSource和
ReloadableResourceBundleMessageSource。前者提供了訪問(wèn)Properties文件的支持,后者
添加了無(wú)需重啟JVM,重新加載Properties文件的支持。
ApplicationContext的國(guó)際化和資源訪問(wèn)支持
1. 如類層次圖所示,在我們的例子中,F(xiàn)ileSystemXmlApplicationContext使用
DefaultListableBeanFactory裝載和解釋testmsg.xml配置文件(參見(jiàn)代碼分析的
BeanFactory部分)。
2. FileSystemXmlApplicationContext根據(jù)配置文件的BeanDefinition創(chuàng)建
ResourceBundleMessageSource,加載<list>元素定義的Properties文件,并保存在
AbstractApplicationContext的屬性中。當(dāng)客戶程序調(diào)用getMessage方法時(shí),
AbstractApplicationContext調(diào)用ResourceBundleMessageSource的getMessage方法返回
Message信息。
3. 至于上節(jié)提到的MessageSource的id只能是messageSource,是因?yàn)?/FONT>
AbstractApplicationContext的initMessageSource()方法中,有這樣一段代碼:
this.messageSource = (MessageSource)
getBean(MESSAGE_SOURCE_BEAN_NAME);
其中MESSAGE_SOURCE_BEAN_NAME的定義為:
static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
原因找到了,其實(shí)只要稍做代碼重構(gòu),即可消除這個(gè)缺陷。
4. 如類層次圖所示,AbstractApplicationContext繼承了DefaultResourceLoader,
當(dāng)客戶程序調(diào)用getResource方法時(shí),使用父類中實(shí)現(xiàn)的方法來(lái)處理。
ApplicationContext的事件傳播
準(zhǔn)備測(cè)試用例
1. 首先編寫測(cè)試用例。
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);
}
}
運(yùn)行測(cè)試案例SenderBeanTest,綠條,測(cè)試通過(guò)。Console窗口出現(xiàn)以下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
事件傳播的實(shí)現(xiàn)
1. FileSystemXmlApplicationContext的構(gòu)造器調(diào)用AbstractApplicationContext的
refresh方法。如圖所示,refresh方法調(diào)用refreshListeners方法。
2. AbstractApplicationContext的refreshListeners方法使用BeanFactory的
getBeanOfType方法得到所有ApplicationListener類(本例中是MessageListener),并使用
addListener方法把它們都放入ApplicationEventMulticasterImpl的Set容器中
(eventListeners變量)。
3. 如圖所示,SenderBean實(shí)現(xiàn)ApplicationContextAware接口,并通過(guò)
setApplicationContext方法注入ApplicationContext對(duì)象實(shí)例。
4. 當(dāng)調(diào)用SenderBean類sendMessage方法時(shí),AbstractApplicationContext調(diào)用
publishEvent方法。
5. AbstractApplicationContext類的publishEvent方法調(diào)用
ApplicationEventMulticasterImpl類的onApplicationEvent方法。
6. ApplicationEventMulticasterImpl通知Set容器中所有的ApplicationListener
對(duì)象,調(diào)用它們的onApplicationEvent方法。
從以上的過(guò)程可以看出,ApplicationContext將事件通知所有的
ApplicationListener。如果ApplicationListener的子類(如MessageListener)只想接受
指定的事件類型,需要自己編寫過(guò)濾代碼,如例子中的if (event instanceof MessageEvent)。