b ApplicationContext额外的能力- messagesource & 事件机制 & resource的访问
文章目录
1.15 附加的功能
org.springframework.beans.factory
包提供了基础的功能去管理多样性的Bean,包括以编程的方式。这个org.springframe.context
包添加了ApplicationContext
接口,来扩展BeanFactory
以提供更多面向框架的风格。很多用户完全以声明式来使用ApplicationContext
接口,但是会依赖Contextloader
去加载ApplicationContext
去为java EE WEB应用做准备。
- 以i18n-style发送消息,通过
MessageSource
接口 - 通过URLS和files等加载资源,通过ResourceLoader接口
- 通过
ApplicationListener
接口来发布消息,通过使用ApplicationEventPublisher
来发送消息 - 层次化的加载多个Context,让每一个位于特殊的layer,就像一个application的web layer,通过
HierarchicalBeanFactory
接口。
15.1 使用MessageSource的初始化
接口方法
ApplicationContext接口集成接口MessageSource
,因此提供了一个国际化的功能i18n
。Spring 也提供了HierarchicalMessageSource
接口–能够层次化的处理消息。这些接口能够提供关于Spring处理消息的解决方案。方法定义的接口包括。
-
String getMessage(String code, Object[] args, String default, Locale loc)
基础的方法用于从MessageSource
获取消息,当没有消息说,会返回一个默认的消息。任何消息都必须符合MessageFormat
消息格式,通过替换MessageFormat
类的属性值。 -
String getMessage(String code, Object[] args, Locale loc)
潜在的和上一个方法提供功能一致,就是没有默认方法。如果没有消息,NoSuchMessageExceotion
就会被抛出 -
String getMessage(MessageSourceResolvable resolvable, Locale locale)
所有属性像前两个方法,这次被包裹在MessageSourceResolvable对象里,如果没有消息,NoSuchMessageExceotion
就会被抛出
加载机制
当一个ApplicationContext被加载的时候,会自动寻找并加载MessageSource
bean。而且这个beMan必须有name:messageSource
。在MessageSource
bean被加载后,那么所有的方法都会被委托给这个bean。如果不能找到,那么一个空的DelegatingMessageSource
就会被初始化。
Spring提供了两种MessageSource
ResourceBundleMessageSource 和 StaticMessageSource。都实现了HierachialMessageSource。StaticMessageSource
很少备用到,但是提供编程化的方式去向Source添加message。
ResourcrBundleMessageSource的示例
这三种值对应的属性文件,放置在classpath
执行代码去验证
总结
MessageSource定义在beans.xml
中,存在classpath的根目录下。messageSource定义了一堆resource bundles属性文件,通过basenames
属性。这三个文件也都相应的存在classpath的根目录下–format.properties,exception.properties和windows.properties。
展示消息被查找的过程
将exception.properties加载到Messagesource。再将MessageSource放置到bean的message属性里面
类
支持i18n国际化标准。MessageSource的实现遵循和JDKResourceBundle
一样的国际化。类如如果你想用British(en-GB)的属性文件,仅需要把属性文件命名为format_en_GB.properties
,exceptions_en_GB.properties
即可。
国际化举例
MessageSourceAware接口去获得MessageSource消息
15.2 Standard and Custom Events
标准的event
自定事件处理。
ApplicationContext事件处理靠ApplicationEvent
和ApplicationListener
接口。如果一个bean 实现了ApplicationListener接口,被注册到了context,每次ApplicationEvent被ApplicationContext发布时,ApplicationListener
会被通知。这是一个标准的观察者模式。
Event | Explanation |
---|---|
ContextRefreshedEvent | 在初始化或刷新ApplicationContext时发布(例如,通过使用ConfigurableApplicationContext接口上的refresh()方法)。在这里,“已初始化”是指所有Bean均已加载,检测到并**了后处理器Bean,已预先实例化单例并且可以使用ApplicationContext对象。只要尚未关闭上下文,只要选定的ApplicationContext实际上支持这种“热”刷新,就可以多次触发刷新。例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext不支持。 |
ContextStartedEvent | 在ConfigurableApplicationContext接口上使用start()方法启动ApplicationContext时发布。在这里,“启动”表示所有Lifecycle bean都收到一个明确的启动信号。通常,此信号用于在显式停止后重新启动Bean,但也可以用于启动尚未配置为自动启动的组件(例如,尚未在初始化时启动的组件)。 |
ContextStoppedEvent | 通过使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext时发布。在这里,“已停止”表示所有Lifecycle bean都收到一个明确的停止信号。停止的上下文可以通过start()调用重新启动。 |
ContextClosedEvent | 通过使用ConfigurableApplicationContext接口上的close()方法或通过JVM关闭钩子关闭ApplicationContext时发布。在这里,“封闭”意味着所有单例豆将被销毁。关闭上下文后,它将达到使用寿命,无法刷新或重新启动。 |
RequestHandledEvent | 一个特定于Web的事件,告诉所有Bean HTTP请求已得到服务。请求完成后,将发布此事件。此事件仅适用于使用Spring的DispatcherServlet的Web应用程序。 |
ServletRequestHandledEvent | RequestHandledEvent的子类,添加了特定于Servlet的上下文信息。 |
自定义的event:邮件的事件配置
要发布自定义ApplicationEvent,请在ApplicationEventPublisher上调用publishEvent()方法。通常,这是通过创建一个实现ApplicationEventPublisherAware的类并将其注册为Spring Bean来完成的。以下示例显示了此类:
在配置时,Spring容器检测到EmailService实现了ApplicationEventPublisherAware并自动调用setApplicationEventPublisher()。实际上,传入的参数是Spring容器本身。您正在通过其ApplicationEventPublisher接口与应用程序上下文进行交互。
ApplicationEvent
要接收自定义的ApplicationEvent,可以创建一个实现ApplicationListener的类并将其注册为Spring Bean。以下示例显示了此类:
ApplicationListener
注意,ApplicationListener通常使用您的自定义事件的类型(上例中的BlackListEvent)进行参数化。这意味着onApplicationEvent()方法可以保持类型安全,从而避免了任何向下转换的需求。您可以根据需要注册任意数量的事件侦听器,但是请注意,默认情况下,事件侦听器会同步接收事件。这意味着publishEvent()方法将阻塞,直到所有侦听器都已完成对事件的处理为止。这种同步和单线程方法的一个优点是,当侦听器收到事件时,如果有可用的事务上下文,它将在发布者的事务上下文内部进行操作。如果有必要采用其他发布事件的策略,请参见Spring的ApplicationEventMulticaster接口的Javadoc和SimpleApplicationEventMulticaster实现的配置选项。
以下示例显示了用于注册和配置上述每个类的Bean定义:
基于注解的事件
4.2版本以上
@EventLister 注册
从Spring 4.2开始,您可以使用@EventListener批注在托管Bean的任何公共方法上注册事件侦听器。 BlackListNotifier可以重写如下:
方法签名再次声明其侦听的事件类型,但是这次使用灵活的名称且未实现特定的侦听器接口。只要实际事件类型在其实现层次结构中解析您的通用参数,也可以通过通用类型来缩小事件类型。
如果您的方法应该侦听多个事件,或者您要完全不使用任何参数来定义它,则事件类型也可以在注释本身上指定。以下示例显示了如何执行此操作:
指定接受的事件类型
SpEL表达式条件化的过滤事件
SpEL表达式可以获得的变量,用来设置条件
Name | Location | Description | Example |
---|---|---|---|
Event | root object | The actual ApplicationEvent. | #root.event or event |
Arguments array | root object | The arguments (as an object array) used to invoke the method. | #root.args or args; args[0] to access the first argument, etc. |
Argument name | evaluation context | 任何方法参数的名称。如果由于某种原因这些名称不可用(例如,由于在编译的字节码中没有调试信息),则还可以使用#a <#arg>语法(其中<#arg>代表参数索引(从0开始)。 | #blEvent or #a0 (you can also use #p0 or #p<#arg> parameter notation as an alias) |
连续处理多个事件的方法:使lisenter返回一个事件即可
异步监听 @Async
如果你想让监听器异步的处理事件,重用这个@Async得支持。
异步事件的限制
- 如果异步事件抛出一个异常,那么它不能够被很好的传递给caller.看
AsyncUncaughtExceptionHandler
- 异步事件监听器,不能靠推push下一个事件来返回真。如果你想要推送另外一个事件作为当前处理的结果。注入一个
ApplicationEventPublisher
来发布事件。
Ordering Listeners 监听器排序
使用Order
泛型事件
您还可以使用泛型来进一步定义事件的结构。考虑使用EntityCreatedEvent ,其中T是创建的实际实体的类型。例如,您可以创建以下侦听器定义以仅接收Person的EntityCreatedEvent
由于类型擦除,只有在触发的事件解析了事件侦听器所依据的通用参数(即类似PersonCreatedEvent的类扩展EntityCreatedEvent {…})时,此方法才起作用。
在某些情况下,如果所有事件都遵循相同的结构,这可能会变得很乏味(就像前面示例中的事件一样)。在这种情况下,您可以实现ResolvableTypeProvider来指导框架,使其超出运行时环境提供的范围。以下事件显示了如何执行此操作:
15.3 对低级Resource的访问
为了获得最佳用法和对应用程序上下文的理解,您应该熟悉Spring的Resource抽象,如参考资料所述。参考Resource一节
应用程序上下文是ResourceLoader,可用于加载Resource对象。 Resource本质上是JDK java.net.URL类的功能更丰富的版本。实际上,资源的实现在适当的地方包装了java.net.URL的实例。资源可以以透明的方式从几乎任何位置获取低级资源,包括从类路径,文件系统位置,可使用标准URL描述的任何位置以及其他一些变体。如果资源位置字符串是没有任何特殊前缀的简单路径,则这些资源的来源是特定的,并且适合于实际的应用程序上下文类型。
您可以配置部署到应用程序上下文中的bean,以实现特殊的回调接口ResourceLoaderAware,以便在初始化时自动调用,并将应用程序上下文本身作为ResourceLoader传入。您还可以公开Resource类型的属性,以用于访问静态资源。它们像其他任何属性一样注入其中。您可以将那些Resource属性指定为简单的String路径,并在部署bean时依靠从这些文本字符串到实际Resource对象的自动转换。
提供给ApplicationContext构造函数的一个或多个位置路径实际上是资源字符串,并且按照特定的上下文实现以简单的形式对其进行适当处理。例如,ClassPathXmlApplicationContext将简单的位置路径视为类路径位置。您也可以使用带有特殊前缀的位置路径(资源字符串)来强制从类路径或URL中加载定义,而不管实际的上下文类型如何。
1.15.4. 加载 Web Applications的ApplicationContext实例化
Convenient ApplicationContext Instantiation for Web Applications
您可以使用例如ContextLoader声明性地创建ApplicationContext实例。当然,您也可以使用ApplicationContext实现之一以编程方式创建ApplicationContext实例。
您可以使用ContextLoaderListener注册ApplicationContext,如以下示例所示:
侦听器检查contextConfigLocation参数。如果参数不存在,那么侦听器将使用/WEB-INF/applicationContext.xml作为默认值。当参数确实存在时,侦听器将使用预定义的定界符(逗号,分号和空格)来分隔String,并将这些值用作搜索应用程序上下文的位置。还支持蚂蚁风格的路径模式。示例包括/WEB-INF/*Context.xml(适用于所有名称以Context.xml结尾且位于WEB-INF目录中的文件)和/ WEB-INF / ** / * Context.xml(适用于所有此类文件)文件在WEB-INF的任何子目录中)。
15.5. Deploying a Spring ApplicationContext as a Java EE RAR File
可以将Spring ApplicationContext部署为RAR文件,将上下文及其所有必需的Bean类和库JAR封装在Java EE RAR部署单元中。这等效于引导独立的ApplicationContext(仅托管在Java EE环境中)能够访问Java EE服务器功能。对于部署无头WAR文件的场景而言,RAR部署是一种更自然的选择-实际上,这种WAR文件没有任何HTTP入口点,仅用于自举Java EE环境中的Spring ApplicationContext。
对于不需要HTTP入口点而仅由消息端点和计划的作业组成的应用程序上下文,RAR部署是理想的选择。在这样的上下文中,Bean可以使用应用程序服务器资源,例如JTA事务管理器和JNDI绑定的JDBC DataSource实例以及JMS ConnectionFactory实例,并且还可以在平台的JMX服务器上注册-整个过程都通过Spring的标准事务管理以及JNDI和JMX支持工具进行。应用程序组件还可以通过Spring的TaskExecutor抽象与应用程序服务器的JCA WorkManager进行交互。
有关RAR部署中涉及的配置详细信息,请参见SpringContextResourceAdapter类的javadoc。
为了将Spring ApplicationContext作为Java EE RAR文件进行简单部署:
1.将所有应用程序类打包到RAR文件(这是具有不同文件扩展名的标准JAR文件)中。将所有必需的库JAR添加到RAR归档文件的根目录中。添加一个META-INF / ra.xml部署描述符(如javadoc中的SpringContextResourceAdapter所示)和相应的Spring XML bean定义文件(通常为META-INF / applicationContext.xml)。
将生成的RAR文件拖放到应用程序服务器的部署目录中。
这种RAR部署单元通常是独立的。它们不会将组件暴露给外界,甚至不会暴露给同一应用程序的其他模块。与基于RAR的ApplicationContext的交互通常是通过与其他模块共享的JMS目标进行的。例如,基于RAR的ApplicationContext也可以安排一些作业或对文件系统(或类似文件)中的新文件做出反应。如果需要允许来自外部的同步访问,则可以(例如)导出RMI端点,该端点可以由同一台计算机上的其他应用程序模块使用。