在前文的介紹中我們知道沸停,org.springframework.beans.factory
包提供了一些基本的功能來(lái)管理和控制Bean鸵鸥,甚至通過(guò)編程的方式來(lái)實(shí)現(xiàn)。org.springframework.context
包添加了ApplicationContext
接口,ApplicationContext
接口擴(kuò)展了BeanFactory
接口施绎。ApplicationContext
接口擴(kuò)展了其他的接口來(lái)以一種更加面向應(yīng)用的方式來(lái)提供額外的功能。很多開發(fā)者喜歡以完全顯式聲明的方式來(lái)使用ApplicationContext
贞绳,不需要通過(guò)編程的方式來(lái)創(chuàng)建ApplicationContext
谷醉,不過(guò)需要依賴于一些支持類,比如J2EE通過(guò)ContextLoader
來(lái)自動(dòng)實(shí)例化ApplicationContext
,啟動(dòng)過(guò)程也是J2EE程序的啟動(dòng)過(guò)程之一。
為了增強(qiáng)BeanFactory
的功能脓鹃,使之變得更加面向應(yīng)用啃匿,context包中還提供如下功能:
- 通過(guò)
MessageSource
接口來(lái)訪問(wèn)i18n的messages - 通過(guò)
ResourceLoader
接口訪問(wèn)資源(比如URL和文件) - 通過(guò)
ApplicationEventPublisher
接口,可以支持時(shí)間發(fā)布(對(duì)應(yīng)Bean需實(shí)現(xiàn)ApplicationListener
接口) - 通過(guò)
HierarchicalBeanFactory
接口來(lái)加載多個(gè)(層次化)context,允許每個(gè)context關(guān)注自己所在的層,比如應(yīng)用的web層
通過(guò)MessageSource來(lái)進(jìn)行國(guó)際化
ApplicationContext
接口擴(kuò)展了一個(gè)名為MessageSource
的接口,來(lái)實(shí)現(xiàn)國(guó)際化(i18n)的功能刃永。Spring也提供了接口HierarchicalMessageSource
接口,可以解析層次化的信息羹应。這些接口在一起提供了Spring的信息解析的基礎(chǔ)揽碘。這些接口中的方法包括:
-
String getMessage(String code, Object[] args, String default, Locale loc)
:該方法用來(lái)從MessageSource
中獲取信息。如果特定的語(yǔ)言沒(méi)有找到消息的話,返回的是默認(rèn)的消息雳刺。任何傳入的參數(shù)都會(huì)通過(guò)標(biāo)準(zhǔn)庫(kù)和MessageFormat
的功能來(lái)變成替換的值劫灶。 -
String getMessage(String code, Object[] args, Locale loc)
: 本質(zhì)上和前面的方法一樣,但是有一個(gè)區(qū)別就是沒(méi)有指定默認(rèn)的返回信息掖桦,如果找不到指定的信息本昏,就會(huì)拋出NoSuchMessageException
異常。 -
String getMessage(MessageSourceResolvable resolvable, Locale locale)
:所有之前方法所用的屬性信息都同時(shí)包裹MessageSourceResolvable
這個(gè)類之中的枪汪,開發(fā)者也可以使用這個(gè)方法涌穆。
當(dāng)ApplicationContext
加載的時(shí)候,會(huì)自動(dòng)搜索context中定義的MessageSource
這個(gè)Bean雀久,該Bean的名字必須為messageSource
宿稀。如果這個(gè)Bean找到了,所有前面調(diào)用的方法都會(huì)代理到這個(gè)MessageSource上面赖捌。如果沒(méi)有發(fā)現(xiàn)messageSource
祝沸,ApplicationContext
會(huì)嘗試去父節(jié)點(diǎn),來(lái)查找對(duì)應(yīng)的Bean越庇。如果找到罩锐,會(huì)將Bean作為MessageSource
來(lái)使用。如果ApplicationContext
無(wú)法找到任何的MessageSource
卤唉,那么就會(huì)使用一個(gè)空的DelegatingMessageSource
來(lái)解決前面那些方法的調(diào)用涩惑。
Spring提供了兩種MessageSource
的實(shí)現(xiàn)方案,ResourceBundleMessageSource
以及StaticMessageSource
桑驱。都實(shí)現(xiàn)了HierarchicalMessageSource
來(lái)支持嵌套message竭恬。StaticMessageSource
很少使用,但是卻讓開發(fā)者可以通過(guò)編程的方式將message增加到源上碰纬。ResourceBundleMessageSource
可以參考如下的例子:
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
在上面的例子當(dāng)中萍聊,假定開發(fā)者配置了綁定到classpath的資源分別是format
问芬,exceptions
以及windows
悦析。任何來(lái)解析message的請(qǐng)求都會(huì)通過(guò)JDK標(biāo)準(zhǔn)的ResourceBundle來(lái)解析message的。假設(shè)上面綁定的屬性如下:
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
下面的這段程序就展示了MessageSource
的執(zhí)行方式此衅。因?yàn)?code>ApplicationContext都是MessageSource
的子接口强戴,所以其實(shí)現(xiàn)都可以轉(zhuǎn)換為MessageSource
接口:
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", null);
System.out.println(message);
}
上面代碼執(zhí)行的結(jié)果將會(huì)是:
Alligators rock!
總結(jié)一下:MessageSource
定義在beans.xml
之中,隨著classpath而加載挡鞍。messageSource
這個(gè)Bean通過(guò)basenames
屬性指向到了一些綁定的資源骑歹。那三個(gè)文件在basenames
屬性的中的名字需要在classpath的root下面,并且叫做format.properties
墨微,exceptions.properties
和windows.properties
才能解析道媚。
下面的例子展示了將參數(shù)傳遞給message來(lái)進(jìn)行屬性查找,這些參數(shù)將會(huì)被轉(zhuǎn)換為String,并插入到查找信息的占位符中最域。
<beans>
<!-- this MessageSource is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.foo.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", null);
System.out.println(message);
}
}
現(xiàn)在谴分,調(diào)用execute()
方法的返回結(jié)果就是
The userDao argument is required.
為了符合國(guó)際化(i18n),Spring的多個(gè)版本的MessageSource
實(shí)現(xiàn)都遵循著相同的locale
解析規(guī)則來(lái)作為JDK的ResourceBundle
镀脂。簡(jiǎn)而言之牺蹄,為了配合前面例子中所定義的messageSource
,如果需要解析薄翅,比如說(shuō)British locale(en-GB
)沙兰,那么開發(fā)者需要?jiǎng)?chuàng)建的properties文件為format_en_GB.properties
,exceptions_en_GB.properties
以及windows_en_GB.properties
。
默認(rèn)情況下翘魄,locale
的解析是由應(yīng)用的運(yùn)行環(huán)境所管理的鼎天。在下面的例子中,我們手動(dòng)指定locale
為British:
# in exceptions_en_GB.properties
argument.required=Ebagum lad, the {0} argument is required, I say, required.
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
那么上面的代碼運(yùn)行的結(jié)果將是:
Ebagum lad, the 'userDao' argument is required, I say, required.
開發(fā)者也可以通過(guò)使用MessageSourceAware
接口來(lái)獲取到MessageSource
的引用暑竟。任何定義在ApplicationContext
中的Bean如果實(shí)現(xiàn)了MessageSourceAware
接口都會(huì)被注入MessageSource
训措。
作為
ResourceBundleMessageSource
的另一個(gè)選擇,Spring提供了一個(gè)類光羞,名為ReloadableResourceBundleMessageSource
绩鸣。這個(gè)類支持類似于基于JDK的ResourceBundleMessageSource
實(shí)現(xiàn),但是使用起來(lái)更為彈性纱兑。尤其是呀闻,它允許從任何Spring資源位置來(lái)加載文件,而不僅僅是classpath潜慎,支持熱重載屬性文件捡多。開發(fā)者可以參考ReloadableResourceBundleMessageSource
的Javadoc了解更多的細(xì)節(jié)。
標(biāo)準(zhǔn)和定制事件
ApplicationContext
中提供事件的處理機(jī)制铐炫,通過(guò)ApplicationEvent
類和ApplicationListener
接口來(lái)實(shí)現(xiàn)垒手。如果Bean實(shí)現(xiàn)了ApplicationListener
接口,那么任何時(shí)候有ApplicationEvent
發(fā)布到ApplicationContext
的事后倒信,都會(huì)通知到該Bean科贬。本質(zhì)上來(lái)說(shuō),這就是一個(gè)標(biāo)準(zhǔn)的觀察者模式鳖悠。
在Spring 4.2的版本之后榜掌,事件的架構(gòu)就已經(jīng)被增強(qiáng)了,提供了一個(gè)基于注解模型的來(lái)發(fā)布任意的事件乘综,這樣Bean也不需要來(lái)繼承
ApplicationEvent
憎账。當(dāng)這樣的一個(gè)對(duì)象發(fā)布,我們將其包裹進(jìn)PayloadApplicationEvent
卡辰。
下表是Spring提供的標(biāo)準(zhǔn)事件:
事件 | 解釋 |
---|---|
ContextRefreshedEvent |
當(dāng)ApplicationContext 初始化或者刷新的時(shí)候發(fā)布胞皱,舉例來(lái)說(shuō)邪意,當(dāng)調(diào)用了ConfigurableApplicationContext 之中的refresh() 方法之后,就會(huì)產(chǎn)生該事件反砌〕保“初始化”在這里的意思就是指當(dāng)加載Bean,后置處理器Bean(post-processor )于颖,預(yù)加載的單例Bean呆贿,以及ApplicationContext 對(duì)象可以使用的時(shí)候。在context沒(méi)有關(guān)閉的事后森渐,可以多次調(diào)用refresh() 做入,一些ApplicationContext 的實(shí)現(xiàn)都支持“熱”刷新。舉例來(lái)說(shuō)同衣,XmlWebApplicationContext 支持熱刷新竟块,但是GenericApplicationContext 不支持。 |
ContextStartedEvent |
當(dāng)ApplicationContext 啟動(dòng)的事后會(huì)發(fā)布耐齐,通過(guò)ConfigurableApplicationContext 接口中的start() 方法來(lái)觸發(fā)浪秘。“啟動(dòng)”在這里就意味著所有的Lifecycle 的Bean都會(huì)收到明確的啟動(dòng)信號(hào)埠况。通常耸携,這個(gè)信號(hào)是用來(lái)在Bean停止之后來(lái)重啟的,但是也能夠用來(lái)啟動(dòng)沒(méi)有配置自動(dòng)啟動(dòng)的組件辕翰,比如夺衍,在初始化過(guò)程中沒(méi)有啟動(dòng)的組件會(huì)隨著start() 啟動(dòng)。 |
ContextStoppedEvent |
當(dāng)ApplicationContext 停止之后發(fā)布喜命,通過(guò)ConfigurableApplicationContext 之中的stop() 方法來(lái)觸發(fā)沟沙。“停止”意味著所有的Lifecycle Bean受到了明確的停止信號(hào)壁榕。停止的context可能再次通過(guò)start() 來(lái)重啟矛紫。 |
ContextClosedEvent |
當(dāng)ApplicationContext 關(guān)閉的時(shí)候發(fā)布,通過(guò)ConfigurableApplicationContext 之中的close() 方法來(lái)觸發(fā)牌里〖找В“關(guān)閉”意味著所有的單例Bean都會(huì)被銷毀。關(guān)閉的context已經(jīng)到了最后的時(shí)刻二庵,無(wú)法刷新或者重啟贪染。 |
RequestHandledEvent |
Web特定的事件,告訴Bean一個(gè)Http請(qǐng)求正在服務(wù)催享。當(dāng)請(qǐng)求完成之后,才發(fā)布該事件哟绊。這個(gè)事件僅僅在使用SpringDispatcherServlet 的時(shí)候才可用因妙。 |
開發(fā)者也可以創(chuàng)建和發(fā)布自己的定制事件痰憎。如下面的例子:
public class BlackListEvent extends ApplicationEvent {
private final String address;
private final String test;
public BlackListEvent(Object source, String address, String test) {
super(source);
this.address = address;
this.test = test;
}
// accessor and other methods...
}
想要發(fā)布一個(gè)定制的ApplicationEvent
,可通過(guò)ApplicationEventPublisher
來(lái)調(diào)用publishEvent()
方法攀涵。通常的做法是創(chuàng)建一個(gè)類铣耘,實(shí)現(xiàn)ApplicationEventPublisherAware
并將其注冊(cè)為Spring的Bean即可,如下:
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blackList;
private ApplicationEventPublisher publisher;
public void setBlackList(List<String> blackList) {
this.blackList = blackList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String text) {
if (blackList.contains(address)) {
BlackListEvent event = new BlackListEvent(this, address, text);
publisher.publishEvent(event);
return;
}
// send email...
}
}
在配置階段以故,Spring容器會(huì)發(fā)現(xiàn)EmailService
實(shí)現(xiàn)了ApplicationEventPublisherAware
接口并自動(dòng)調(diào)用setApplicationEventPublisher()
方法蜗细。實(shí)際上,參數(shù)會(huì)由Spring容器本身來(lái)傳遞進(jìn)去,開發(fā)者只是通過(guò)ApplicationEventPublisher
接口來(lái)跟應(yīng)用上下文進(jìn)行交互怒详。
如果需要接收到定制的ApplicationEvent
炉媒,需要?jiǎng)?chuàng)建一個(gè)雷來(lái)實(shí)現(xiàn)ApplicationListener
并且將其注冊(cè)為Spring的Bean。代碼如下:
public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
需要注意的是ApplicationListener
是一個(gè)基于參數(shù)泛型化得接口昆烁,BlackListEvent
就是前面所自定義的一個(gè)ApplicationEvent
吊骤,這意味著onApplicationEvent()
方法仍然是類型安全的,避免了向下的類型轉(zhuǎn)換静尼。開發(fā)可以隨意注冊(cè)事件監(jiān)聽器白粉,但是需要注意的是,默認(rèn)情況下鼠渺,事件監(jiān)聽器接受事件是同步的鸭巴。這也意味著除非所有的監(jiān)聽器都處理完了事件,否則publishEvent()
方法會(huì)一直阻塞拦盹。同步的優(yōu)勢(shì)在于當(dāng)監(jiān)聽器接收到事件的時(shí)候奕扣,監(jiān)聽器對(duì)事件的處理都在發(fā)布者的事物內(nèi)部進(jìn)行處理。
下面的代碼將展示如何使用前面定義的類:
<bean id="emailService" class="example.EmailService">
<property name="blackList">
<list>
<value>known.spammer@example.org</value>
<value>known.hacker@example.org</value>
<value>john.doe@example.org</value>
</list>
</property>
</bean>
<bean id="blackListNotifier" class="example.BlackListNotifier">
<property name="notificationAddress" value="blacklist@example.org"/>
</bean>
上面的XML代碼和前面的Bean定義結(jié)合在一起的時(shí)候掌敬,當(dāng)調(diào)用了emailService
的sendEmail()
方法惯豆,那么只要有XML種列舉的黑名單,那么BlackListEvent
就會(huì)發(fā)布奔害。blackListNotifier
Bean作為監(jiān)聽器這樣就會(huì)接收到BlackListEvent
楷兽。
Spring的事件機(jī)制的設(shè)計(jì)初衷是為了讓Spring的Bean和應(yīng)用上下文進(jìn)行簡(jiǎn)單的交互。然而华临,在很多復(fù)雜的企業(yè)集成需求上芯杀,可以使用Spring Intergration,它提供了更好的支持雅潭,可以用來(lái)構(gòu)建輕量級(jí)揭厚,面向模式,事件驅(qū)動(dòng)的Spring編程模型扶供。
基于注解的事件監(jiān)聽器
在Spring的4.2版本后筛圆,事件的監(jiān)聽器就可以通過(guò)在Bean的任何公開方法上面使用EventListener
注解來(lái)實(shí)現(xiàn)了。BlackListNotifier
也可以通過(guò)如下方式來(lái)重寫:
public class BlackListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
從上面的代碼可以看到椿浓,在方法的簽名上面就會(huì)自動(dòng)指定監(jiān)聽的類型太援。當(dāng)然闽晦,這個(gè)監(jiān)聽器也也支持使用泛型參數(shù)。
如果開發(fā)者的方法只是監(jiān)聽部分事件或者開發(fā)者不想使用事件參數(shù)提岔,也可以通過(guò)注解來(lái)聲明事件的類型:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
}
當(dāng)然仙蛉,通過(guò)condition
屬性來(lái)增加運(yùn)行時(shí)的過(guò)濾也是可以的〖蠲桑可以通過(guò)SpEL
表達(dá)式來(lái)進(jìn)行匹配荠瘪。
參考代碼如下:
@EventListener(condition = "#event.test == 'foo'")
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
SpEL
表達(dá)式會(huì)再一次檢查上下文。下表列出了context可見的一些項(xiàng)目赛惩,來(lái)用于條件的事件處理:
Name | Location | Description | Example |
---|---|---|---|
event | 根對(duì)象 | 實(shí)際的ApplicationEvent
|
#root.event |
args | 根對(duì)象 | 用來(lái)調(diào)用目標(biāo)的參數(shù)(數(shù)組) | #root.args[0] |
argument name | 評(píng)估上下文 | 任何方法的參數(shù)哀墓。在某些情況下,名字是不可見的坊秸。(比如說(shuō)非debug的情況下)麸祷,但是參數(shù)的名字仍然是可見的。在#a<#arg> 中褒搔,#arg 表示的是參數(shù)的索引(從0開始) |
#iban 或者#a0
|
需要注意的是#root.event
允許開發(fā)者訪問(wèn)潛在的事件阶牍,甚至方法會(huì)直接引用到發(fā)布的對(duì)象。
如果開發(fā)者需要將發(fā)布的事件作為結(jié)果傳遞給其他方法也是允許的星瘾,只需要修改方法簽名走孽,返回發(fā)布的對(duì)象即可:
@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
需要注意的是,這個(gè)特性不支持異步監(jiān)聽器
上面的新方法將會(huì)發(fā)布一個(gè)新的ListUpdateEvent
來(lái)給處理BlackListEvent
的方法琳状。如果開發(fā)者需要發(fā)布多個(gè)方法磕瓷,返回事件的集合即可。
異步監(jiān)聽器
如果開發(fā)者需要以異步的方式來(lái)處理事件念逞,那么可以使用@Async
注解:
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
// BlackListEvent is processed in a separate thread
}
但是需要注意使用異步事件的一些限制:
- 如果事件監(jiān)聽器拋出異常困食,它是不會(huì)將異常信息傳遞給調(diào)用方的,可以參考
AsyncUncaughtExceptionHandler
來(lái)了解更多的細(xì)節(jié) - 異步監(jiān)聽器無(wú)法發(fā)送返回信息翎承。如果開發(fā)者需要知道處理事件的結(jié)果的話硕盹,只能注入
ApplicationEventPublisher
來(lái)手動(dòng)發(fā)送事件
對(duì)監(jiān)聽器排序
有些時(shí)候,開發(fā)者需要指定監(jiān)聽器調(diào)用的前后順序叨咖,可以通過(guò)增加@Order
注解來(lái)完成:
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
泛型事件
在一些情況下瘩例,開發(fā)者可能希望通過(guò)使用泛型來(lái)進(jìn)一步定義事件的結(jié)構(gòu)〉楦鳎考慮EntityCreatedEvent<T>
事件垛贤,T
表示的則是實(shí)際創(chuàng)建的實(shí)體。開發(fā)者可以創(chuàng)建如果的監(jiān)聽器定義來(lái)僅僅接受Person
對(duì)象的EntityCreatedEvent
事件:
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
...
}
由于函數(shù)簽名對(duì)方法參數(shù)類型的敏感趣倾,上面的方法只會(huì)當(dāng)事件匹配了泛型的時(shí)候才能執(zhí)行聘惦,其他的類型則會(huì)過(guò)濾掉。(只有一些類型形如class PersonCreatedEvent extends EntityCreatedEvent<Person>
之類的才能進(jìn)行事件的處理)
在有些情況下誊酌,這一點(diǎn)也會(huì)變得很麻煩部凑,比如如果所有的事件都是遵循這類結(jié)構(gòu)的話露乏,因?yàn)榉盒蛥?shù)無(wú)法傳入所以就需要寫很多的事件處理函數(shù)碧浊。在這種情況下可以通過(guò)實(shí)現(xiàn)ResolveableTypeProvider
來(lái)令框架能夠在運(yùn)行環(huán)境提供處理:
public class EntityCreatedEvent<T>
extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(),
ResolvableType.forInstance(getSource()));
}
}
ResolvableType
不僅僅支持ApplicationEvent
涂邀,任何作為事件發(fā)送的對(duì)象都能支持。
low-level資源的便捷訪問(wèn)
為了優(yōu)化使用和理解應(yīng)用的上下文箱锐,開發(fā)者應(yīng)該了解Spring針對(duì)Resource
的抽象比勉。
一個(gè)應(yīng)用的上下文就是一個(gè)ResourceLoader
,可以用來(lái)加載Resource
驹止。Resource
在本質(zhì)上就是一個(gè)功能更豐富的JDK的類java.net.URL
浩聋,實(shí)際上,Resource
的實(shí)現(xiàn)之中就包含了一個(gè)java.net.URL
的實(shí)例臊恋。Resource
可以通過(guò)一種透明的方式獲取幾乎任何位置的low-level的資源衣洁,包括從classpath,文件系統(tǒng)地址抖仅,或者任何通過(guò)標(biāo)準(zhǔn)URL來(lái)描述的位置以及一些其他的變化等坊夫。如果資源地址僅僅是一個(gè)簡(jiǎn)單的路徑二沒(méi)有特殊的前綴的話,那么那些資源就是來(lái)自于特指的應(yīng)用上下文的類型撤卢。
開發(fā)者可以將實(shí)現(xiàn)一些特殊的回調(diào)接口的Bean配置發(fā)布到到應(yīng)用上下文环凿。ResourceLoaderAware
,當(dāng)Bean實(shí)現(xiàn)了該接口放吩,將會(huì)在初始化的時(shí)候講應(yīng)用上下文一起傳遞智听,作為ResourceLoader
。開發(fā)者也可以將Resource
的屬性曝露出來(lái)渡紫,來(lái)訪問(wèn)靜態(tài)資源到推,它們和注入的其它屬性是一樣的。開發(fā)者也可以特指那些Resource
的屬性為一些簡(jiǎn)單的字符串路徑惕澎,并依賴于特殊的JavaBeanPropertyEditor
莉测,可以自動(dòng)由上下文注冊(cè),然后將Resouce
解析為實(shí)際的的資源對(duì)象集灌。
ApplicationContext
中所支持的路徑其實(shí)就是資源字符串悔雹,在簡(jiǎn)單的模式下,其實(shí)就近乎為特定的context實(shí)現(xiàn)欣喧。ClassPathXmlApplicationContext
將簡(jiǎn)單的路徑視作classpath路徑的腌零。開發(fā)者也可以使用定位路徑(資源字符串)通過(guò)一些特定的前綴來(lái)聲明路徑是classpath還是URL。
Web應(yīng)用的ApplicationContext實(shí)例化
在Web應(yīng)用中唆阿,開發(fā)者可以通過(guò)使用ContextLoader
來(lái)創(chuàng)建加載一個(gè)ApplicationContext
益涧。當(dāng)然,開發(fā)者也可以通過(guò)編程的方式驯鳖,通過(guò)ApplicationContext
的一些實(shí)現(xiàn)來(lái)創(chuàng)建ApplicationContext
闲询。
開發(fā)者可以通過(guò)使用ContextLoaderListener
來(lái)注冊(cè)ApplicationContext
,代碼如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
該監(jiān)聽器會(huì)檢查contextConfigLocation
參數(shù)久免,如果參數(shù)不存在,監(jiān)聽器會(huì)使用/WEB-INF/applicationContext.xml
作為默認(rèn)路徑扭弧。當(dāng)路徑存在時(shí)阎姥,監(jiān)聽器會(huì)通過(guò)預(yù)定義的分隔符(逗號(hào),分號(hào)以及空格)分割路徑鸽捻,并將分割的路徑作為實(shí)際應(yīng)用檢索的上下文路徑呼巴。Ant風(fēng)格的路徑也是支持的,比如/WEB-INF/*Context.xml
會(huì)匹配所有以Context.xml
為結(jié)尾且在WEB-INF
目錄下的文件御蒲,而WEB-INF/**/*Context.xml
則會(huì)匹配所有WEB-INF
下的子目錄衣赶。
通過(guò)J2EE RAR文件部署Spring ApplicationContext
將Spring的上下文打包成RAR文件也是可以部署的,可以將context以及所有需要的Bean的類和依賴jar包打包成J2EE RAR部署單元厚满。這也等同于啟動(dòng)一個(gè)獨(dú)立的ApplicationContext府瞄,只是由J2EE環(huán)境來(lái)host,ApplicationContext可以訪問(wèn)J2EE服務(wù)設(shè)施碘箍。RAR的部署也是部署WAR場(chǎng)景的一種更為自然的選擇遵馆,實(shí)際上,不包含HTTP訪問(wèn)的WAR文件也僅僅是在J2EE環(huán)境啟動(dòng)Spring的ApplicationContext敲街。
RAR的部署對(duì)于不需要HTTP的的任務(wù)是更為理想的团搞,比如說(shuō)定時(shí)任務(wù)之類的。Bean在這種上下文可以使用應(yīng)用服務(wù)器的資源諸如JTA事物管理器和JNDI綁定的JDBC數(shù)據(jù)源多艇,JMS連接工廠實(shí)例逻恐,或者注冊(cè)平臺(tái)的JMX服務(wù)器,這些通過(guò)Spring標(biāo)準(zhǔn)的事務(wù)管理和JNDI峻黍,JMX支持的特性即可复隆。應(yīng)用組件同樣可以通過(guò)Spring的TaskExecutor
抽象來(lái)進(jìn)行協(xié)同工作。
可以通過(guò)查看SpringContextResourceAdapter
類來(lái)了解RAR文件部署的一些詳細(xì)信息姆涩。
對(duì)于簡(jiǎn)單的部署J2EE的RAR文件:將所有的應(yīng)用類都打包到一個(gè)RAR文件中即可挽拂。將其依賴的jar包也放到RAR文件的root處,增加一個(gè)META-INF/ra.xml
文件(參考SpringContextResourceAdapter
的Javadoc)也將關(guān)聯(lián)的Spring XML的Bean定義文件置于其中骨饿,然后將RAR文件放到應(yīng)用服務(wù)器的發(fā)布目錄即可亏栈。
這樣的RAR文件的部署是自包含的,他們不會(huì)暴露組件給外部宏赘,甚至是同一個(gè)應(yīng)用的其它模塊都不需要引用绒北。與基于RAR的ApplicationContext進(jìn)行交互通常是通過(guò)JMS來(lái)和其它模塊進(jìn)行交互〔焓穑基于RAR的ApplicationContext也可能做些定時(shí)任務(wù)闷游,針對(duì)文件系統(tǒng)中的新文件進(jìn)行處理等工作。如果需要允許支持外部的同步訪問(wèn),可以接入RMI服務(wù)脐往。