本文共 13823 字,大约阅读时间需要 46 分钟。
正如本章介绍中所讨论的,该org.springframework.beans.factory 包提供了用于管理和操作bean的基本功能,包括以编程方式。该org.springframework.context软件包添加了 ApplicationContext 扩展BeanFactory界面的界面,以及扩展其他界面以提供更多应用程序框架导向风格的附加功能。许多人使用ApplicationContext完全声明的方式,甚至没有以编程方式创建它,而是依赖支持类ContextLoader来自动实例化 ApplicationContextJava EE Web应用程序正常启动过程的一部分。
为了增强BeanFactory面向框架的风格的功能,上下文包还提供以下功能:加载多个(分层)上下文,通过HierarchicalBeanFactory接口允许每个上下文关注某个特定层,例如应用程序的Web层 。
该ApplicationContext接口扩展了一个称为的接口MessageSource,因此提供了国际化(i18n)功能。Spring还提供了HierarchicalMessageSource可以分层解析消息的接口。这些接口一起为Spring特效消息解析提供了基础。这些接口上定义的方法包括:
当一个ApplicationContext被加载时,它会自动搜索MessageSource 上下文中定义的一个bean。这个bean必须有名字messageSource。如果找到这样的一个bean,所有对前面方法的调用都被委托给消息源。如果找不到消息源,则ApplicationContext尝试查找包含具有相同名称的bean的父项。如果是这样,它使用该bean作为MessageSource。如果 ApplicationContext无法找到任何消息源,DelegatingMessageSource则会实例化一个空 以便能够接受对上面定义的方法的调用。
Spring提供了两个MessageSource实现,ResourceBundleMessageSource并且 StaticMessageSource。两者都是HierarchicalMessageSource为了做嵌套消息传递而实现的。这StaticMessageSource是很少使用,但提供了编程方式来添加消息到源。在ResourceBundleMessageSource被示出在下面的例子:
format exceptions windows
在这个例子中,假设你在你的类路径中定义了三个资源包,分别叫做format,exceptions和windows。任何解析消息的请求都将以通过ResourceBundles解析消息的JDK标准方式进行处理。出于示例的目的,假设上述两个资源包文件的内容是……
# in format.propertiesmessage=Alligators rock!
# in exceptions.propertiesargument.required=The { 0} argument is required.
MessageSource下一个示例显示了执行功能的程序。请记住,所有ApplicationContext实现都是MessageSource 实现,因此可以转换为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);}
从上述程序产生的输出将是…
Alligators rock!
总之,这个MessageSource被定义在一个叫做的文件中beans.xml,它存在于你的类路径的根目录下。该messageSourcebean定义是指通过它的一些资源包的basenames属性。这是在列表中传递的三个文件basenames属性存在于你的classpath根目录的文件,被称为format.properties,exceptions.properties和 windows.properties分别。
下一个示例显示传递给消息查找的参数; 这些参数将转换为字符串并插入查找消息中的占位符。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); }}
调用该execute()方法的结果输出将是…
The userDao argument is required.
关于国际化(i18n),Spring的各种MessageSource 实现遵循与标准JDK相同的区域设置分辨率和回退规则 ResourceBundle。总之,和继续该示例messageSource先前定义的,如果你想解析British(消息en-GB)语言环境中,您将创建文件名为format_en_GB.properties,exceptions_en_GB.properties和 windows_en_GB.properties分别。
通常,区域设置解析由应用程序的周围环境管理。在这个例子中,(英国)消息将被解析的地区是手动指定的。
#在exceptions_en_GB.properties中argument.required = Ebagum lad,我认为需要{ 0}参数。
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);}
从上述程序运行得到的输出将是…
Ebagum lad, the 'userDao' argument is required, I say, required.
您还可以使用该MessageSourceAware界面来获取MessageSource已定义的任何参考 。任何在ApplicationContext实现MessageSourceAware接口的bean中定义 MessageSource的bean都会在创建和配置bean时注入应用程序上下文。
作为一种选择ResourceBundleMessageSource,Spring提供了一个 ReloadableResourceBundleMessageSource类。该变体支持相同的包文件格式,但比标准的基于JDK的ResourceBundleMessageSource实现更灵活 。特别是,它允许从任何Spring资源位置(而不仅仅是从类路径)读取文件,并支持热重载bundle属性文件(同时有效地缓存它们)。
ApplicationContext通过ApplicationEvent 类和ApplicationListener接口提供事件处理。如果实现ApplicationListener接口的beanA 部署到上下文中,则每次 ApplicationEvent发布到该ApplicationContextbean时,都会通知该beanA。实质上,这是标准Observer设计模式。
Spring提供了以下标准事件:Event | Explanation |
---|---|
ContextRefreshedEvent | 在ApplicationContext上下文中初始化或者刷新。例如,使用ConfigurableApplicationContext中的refresh()方法。这里的“初始化”意味着所有的bean都被加载,检测并激活后处理器bean,单例被预先实例化,并且该ApplicationContext对象已准备好使用。只要上下文尚未关闭,刷新可以多次触发,前提是所选内容ApplicationContext实际上支持“热”刷新。例如,XmlWebApplicationContext支持热点刷新,但GenericApplicationContext不支持 。 |
ContextStartedEvent | 在ApplicationContext启动时发布,使用ConfigurableApplicationContext上下文中的start()方法。这里的“开始”意味着所有的Lifecycle bean都会收到明确的启动信号。通常,此信号用于在显式停止后重新启动Bean,但它也可用于启动尚未配置为自动启动的组件,例如尚未启动初始化的组件。 |
ContextStoppedEvent | 在ApplicationContext停止时发布,使用ConfigurableApplicationContext上下文中的stop()方法。这里“停止”意味着所有的Lifecycle bean都会收到明确的停止信号。停止的上下文可以通过start()呼叫重新启动 。 |
ContextClosedEvent | 在ApplicationContext关闭时发布,使用界面close()上的方法 ConfigurableApplicationContext。这里的“关闭”意味着所有的单例bean被销毁。封闭的环境达到其生命的尽头; 它不能被刷新或重新启动。 |
RequestHandledEvent | 一个特定于web的事件,告知所有bean HTTP请求已被服务。此事件在请求完成后发布。此事件仅适用于使用Spring的Web应用程序DispatcherServlet。 |
您还可以创建和发布自己的自定义事件。这个例子演示了一个扩展Spring ApplicationEvent基类的简单类:
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...}
public class EmailService implements ApplicationEventPublisherAware { private ListblackList; private ApplicationEventPublisher publisher; public void setBlackList(List 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容器将检测到该EmailService实现 ApplicationEventPublisherAware并将自动调用 setApplicationEventPublisher()。实际上,传入的参数将是Spring容器本身; 你只是通过它的ApplicationEventPublisher接口与应用程序上下文进行 交互。
public class BlackListNotifier implements ApplicationListener{ private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } public void onApplicationEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress... }}
请注意,ApplicationListener它通常用您的自定义事件的类型进行参数化BlackListEvent。这意味着该onApplicationEvent()方法可以保持类型安全,避免任何向下转换的需要。您可以根据需要注册许多事件侦听器,但请注意,默认情况下事件侦听器会同步接收事件。这意味着publishEvent()方法会阻塞,直到所有听众完成处理事件。这种同步和单线程方法的一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布者的事务上下文内部运行。如果需要另一个事件发布策略,请参考Spring ApplicationEventMulticaster界面的javadoc 。
以下示例显示了用于注册和配置上述每个类的bean定义:
known.spammer@example.org known.hacker@example.org john.doe@example.org
综合起来,当调用bean 的sendEmail()方法时emailService,如果有任何应该被列入黑名单的电子邮件,BlackListEvent则会发布类型的自定义事件 。这个blackListNotifierbean被注册为一个 ApplicationListener并且因此接收到BlackListEvent,在此时它可以通知适当的各方。
Spring的事件机制被设计为在同一个应用程序上下文中的Spring bean之间进行简单的通信。然而,对于更复杂的企业集成需求,单独维护的 Spring Integration项目为构建轻量级,面向模式的事件驱动架构提供完全支持, 该架构基于着名的Spring编程模型。
从Spring 4.2开始,可以通过@EventListener
注释在托管bean的任何公共方法上注册事件侦听器。该BlackListNotifier可改写如下:
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... }}
正如您在上面看到的,方法签名再次声明它监听的事件类型,但是这次使用灵活的名称并且不实现特定的监听器接口。只要实际事件类型在其实现层次结构中解析泛型参数,事件类型也可以通过泛型进行缩小。
如果你的方法应该监听几个事件,或者如果你想要根本没有参数定义它,事件类型也可以在注释本身上指定:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})public void handleContextStart() { ...}
也可以通过condition注释的属性添加额外的运行时过滤,该过滤器定义了一个SpEL表达式,该表达式应匹配以实际调用特定事件的方法。
例如,如果test事件的属性等于foo:我们的通知器可以被重写为仅被调用:
@EventListener(condition = "#blEvent.test == 'foo'")public void processBlackListEvent(BlackListEvent blEvent) { // notify appropriate parties via notificationAddress...}
每个SpEL表达式再次评估一个专用的上下文。下表列出了可用于上下文的项目,以便可以将它们用于条件事件处理:
Table 8. Event SpEL available metadataName | Location | Description | Example |
---|---|---|---|
Event | root object | The actual ApplicationEvent | root.event |
root.event | root.event | 用于调用目标的参数(如数组) | 用于调用目标的参数(如数组) |
Argument name | Argument name | 任何方法参数的名称。如果由于某种原因名称是不可用(例如,没有调试信息),参数名称也是在现有的#a<#arg> 地方#arg代表的说法指数(从0开始)。 | blEvent或者#a0(也可以使用#p0或#p<#arg>标记作为别名) |
注意#root.event,即使您的方法签名实际引用了已发布的任意对象,也可以访问基础事件。
如果您需要发布一个事件作为处理另一个事件的结果,只需更改方法签名以返回应该发布的事件,如下所示:
@EventListenerpublic ListUpdateEvent handleBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress and // then publish a ListUpdateEvent...}
异步监听器不支持这个东西
这种新方法将为上述方法的ListUpdateEvent每个BlackListEvent处理发布一个新的方法。如果您需要发布多个事件,则只需返回一些Collection事件。
如果您希望特定的侦听器异步处理事件,simply reuse the regular @Async
support:
@EventListener@Asyncpublic void processBlackListEvent(BlackListEvent event) { // BlackListEvent is processed in a separate thread}
使用异步事件时请注意以下限制:
如果事件监听器抛出Exception它不会传播给调用者,请检查AsyncUncaughtExceptionHandler更多细节。 这种事件监听器不能发送回复。如果您需要发送另一个事件作为处理结果,请注入ApplicationEventPublisher以手动发送事件。如果您需要在另一个之前调用侦听器,只需将该@Order
注释添加到方法声明中:
@EventListener@Order(42)public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress...}
您也可以使用泛型来进一步定义事件的结构。考虑 创建实际实体的类型 EntityCreatedEvent<T>
在哪里T。您可以创建以下侦听器定义以仅接收EntityCreatedEvent
以下内容 Person:
@EventListenerpublic void onPersonCreated(EntityCreatedEventevent) { ...}
由于类型擦除,只有当被触发的事件解析了事件侦听器过滤的泛型参数时(这是类似的class PersonCreatedEvent extends EntityCreatedEvent<Person> { … })
,这才会起作用 。
在某些情况下,如果所有事件都遵循相同的结构(这应该是上述事件的情况),则这可能变得非常乏味。在这种情况下,您可以实现ResolvableTypeProvider
以引导框架超出运行时环境所提供的范围:
public class EntityCreatedEventextends ApplicationEvent implements ResolvableTypeProvider { public EntityCreatedEvent(T entity) { super(entity); } @Override public ResolvableType getResolvableType() { return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource())); }}
This works not only for ApplicationEvent but any arbitrary object that you’d send as an event.
为了最佳使用和理解应用程序上下文,用户通常应该熟悉Spring的Resource抽象,如“ 资源 ”一章所述 。
应用程序上下文是一个ResourceLoader可以用来加载Resources 的应用程序上下文。A Resource本质上是JDK类的功能更丰富的版本java.net.URL,实际上,在适当Resource的java.net.URL地方包装一个实例。A Resource可以以透明的方式从几乎任何位置获取底层资源,包括类路径,文件系统位置,任何可用标准URL描述的地方以及其他一些变体。如果资源位置字符串是一个没有任何特殊前缀的简单路径,那么这些资源来自特定且适合于实际应用程序上下文类型。
您可以配置一个部署到应用程序上下文中的bean来实现特殊的回调接口,ResourceLoaderAware在初始化时自动调用回应用程序上下文本身作为 ResourceLoader。您还可以公开Resource用于访问静态资源的类型属性; 它们将像其他任何属性一样被注入到它中。您可以将这些Resource属性指定为简单的String路径,并依赖PropertyEditor由上下文自动注册的特殊JavaBean ,以便Resource在部署Bean时将这些文本字符串转换为实际对象。
提供给ApplicationContext构造函数的位置路径实际上是资源字符串,并且以简单形式适当地处理特定的上下文实现。ClassPathXmlApplicationContext将简单的位置路径视为类路径位置。您还可以使用带有特殊前缀的位置路径(资源字符串)来强制从类路径或URL中加载定义,而不管实际的上下文类型如何。
您可以ApplicationContext通过使用例如a来声明性地创建实例 ContextLoader。当然,您也可以ApplicationContext使用其中一种ApplicationContext实现方式编程创建实例。
您可以ApplicationContext使用ContextLoaderListener如下注册一个:contextConfigLocation /WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml org.springframework.web.context.ContextLoaderListener
监听者检查contextConfigLocation参数。如果该参数不存在,则侦听器将/WEB-INF/applicationContext.xml用作默认值。当参数确实存在时,侦听器使用预定义的分隔符(逗号,分号和空白)来分隔字符串,并将这些值用作应用程序上下文将被搜索的位置。也支持Ant风格的路径模式。例子是/WEB-INF/Context.xml名称以“Context.xml”结尾的所有文件,驻留在“WEB-INF”目录中,并且/WEB-INF/*/*Context.xml对于“WEB-INF”的任何子目录中的所有这些文件。
可以将Spring ApplicationContext部署为RAR文件,将上下文及其所有必需的bean类和库JAR封装到Java EE RAR部署单元中。这相当于引导了一个独立的ApplicationContext,它只是在Java EE环境中托管,能够访问Java EE服务器设施。RAR部署是部署无头WAR文件的场景中更自然的选择,实际上,WAR文件没有任何HTTP入口点,仅用于在Java EE环境中引导Spring ApplicationContext。
RAR部署非常适合不需要HTTP入口点但仅包含消息端点和预定作业的应用程序上下文。在这种情况下,Bean可以使用应用服务器资源,例如JTA事务管理器和JNDI绑定的JDBC DataSources和JMS ConnectionFactory实例,也可以通过Spring的标准事务管理和JNDI和JMX支持工具向平台的JMX服务器注册。应用程序组件还可以通过Spring的TaskExecutor抽象与应用程序服务器的JCA WorkManager进行交互。
要将Spring ApplicationContext简单部署为Java EE RAR文件:将所有应用程序类打包到RAR文件中,该文件是具有不同文件扩展名的标准JAR文件。将所有必需的库JAR添加到RAR归档的根目录中。添加一个“META-INF / ra.xml”部署描述符(如SpringContextResourceAdapters javadoc中所示)和相应的Spring XML bean定义文件(通常为“META-INF / applicationContext.xml”),并放弃生成的RAR文件到您的应用程序服务器的部署目录。
这种RAR部署单元通常是独立的; 它们不会将组件暴露给外部世界,甚至不会暴露给同一应用程序的其他模块。与基于RAR的ApplicationContext的交互通常通过它与其他模块共享的JMS目标发生。例如,基于RAR的ApplicationContext也可以调度一些作业,对文件系统中的新文件(或诸如此类)作出反应。如果需要允许从外部进行同步访问,则可以导出RMI端点,这当然可以由同一台机器上的其他应用程序模块使用。
好啦,ApplicationContext的附加功能,就是这么多东西,平时多看看,在用的时候,就能得心应手。
转载地址:http://xslul.baihongyu.com/