Spring核心技術(shù)(十四)——ApplicationContext的額外功能

在前文的介紹中我們知道沸停,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.propertieswindows.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ā)沟沙。“停止”意味著所有的LifecycleBean受到了明確的停止信號(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)用了emailServicesendEmail()方法惯豆,那么只要有XML種列舉的黑名單,那么BlackListEvent就會(huì)發(fā)布奔害。blackListNotifierBean作為監(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
}

但是需要注意使用異步事件的一些限制:

  1. 如果事件監(jiān)聽器拋出異常困食,它是不會(huì)將異常信息傳遞給調(diào)用方的,可以參考AsyncUncaughtExceptionHandler來(lái)了解更多的細(xì)節(jié)
  2. 異步監(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ù)脐往。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末休吠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子业簿,更是在濱河造成了極大的恐慌瘤礁,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辖源,死亡現(xiàn)場(chǎng)離奇詭異蔚携,居然都是意外死亡希太,警方通過(guò)查閱死者的電腦和手機(jī)克饶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)誊辉,“玉大人矾湃,你說(shuō)我怎么就攤上這事《槌危” “怎么了邀跃?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蛙紫。 經(jīng)常有香客問(wèn)我拍屑,道長(zhǎng),這世上最難降的妖魔是什么坑傅? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任僵驰,我火速辦了婚禮,結(jié)果婚禮上唁毒,老公的妹妹穿的比我還像新娘蒜茴。我一直安慰自己,他們只是感情好浆西,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布粉私。 她就那樣靜靜地躺著,像睡著了一般近零。 火紅的嫁衣襯著肌膚如雪诺核。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天久信,我揣著相機(jī)與錄音窖杀,去河邊找鬼。 笑死入篮,一個(gè)胖子當(dāng)著我的面吹牛陈瘦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼痊项,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锅风!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鞍泉,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤皱埠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后咖驮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體边器,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年托修,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忘巧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睦刃,死狀恐怖砚嘴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涩拙,我是刑警寧澤际长,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站兴泥,受9級(jí)特大地震影響工育,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搓彻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一如绸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧好唯,春花似錦竭沫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至靶端,卻和暖如春谎势,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杨名。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工脏榆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人台谍。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓须喂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坞生,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容