1.15. Additional Capabilities of the ApplicationContext

牛凌
2023-12-01

上一节 1.14. Registering a LoadTimeWeaver

目录

下一节 1.15. Additional Capabilities of the ApplicationContext

1.15. Additional Capabilities of the ApplicationContext

1.15. ApplicationContext的附加功能

As discussed in the chapter introduction, the org.springframework.beans.factory package provides basic functionality for managing and manipulating beans, including in a programmatic way. The org.springframework.context package adds the ApplicationContext interface, which extends the BeanFactory interface, in addition to extending other interfaces to provide additional functionality in a more application framework-oriented style. Many people use the ApplicationContext in a completely declarative fashion, not even creating it programmatically, but instead relying on support classes such as ContextLoader to automatically instantiate an ApplicationContext as part of the normal startup process of a Java EE web application.

正如在引言中所讨论的,org.springframework.beans.factory 包提供了
包括以编程的方式管理和操作bean的基本功能。
org.springframework.context 包添加了ApplicationContext接口,
它扩展了BeanFactory接口,此外还扩展了其他接口,
以更加面向应用程序框架的风格提供额外的功能。
许多人以一种完全声明式的方式使用ApplicationContext,甚至不是
通过编程来创建它,而是依赖于支持类(如ContextLoader)来自动实例化
一个ApplicationContext,作为Java EE web应用程序的正常启动过程的一部分。

To enhance BeanFactory functionality in a more framework-oriented style, the context package also provides the following functionality:

  • Access to messages in i18n-style, through the MessageSource interface.
  • Access to resources, such as URLs and files, through the ResourceLoader interface.
  • Event publication, namely to beans that implement the ApplicationListener interface, through the use of the ApplicationEventPublisher interface.
  • Loading of multiple (hierarchical) contexts, letting each be focused on one particular layer, such as the web layer of an application, through the HierarchicalBeanFactory interface.
为了以更加面向框架的风格增强BeanFactory的功能,context 包还提供了以下功能:
* 通过MessageSource接口访问i18n风格的消息。 
* 通过ResourceLoader接口访问资源,例如url和文件。
* 事件发布,即通过使用ApplicationEventPublisher接口发布到
  实现ApplicationListener接口的bean。
* 通过HierarchicalBeanFactory接口加载多个(层次结构的) contexts,
  让每个contexts都关注于一个特定的层,比如应用程序的web层。

1.15.1. Internationalization using MessageSource

The ApplicationContext interface extends an interface called MessageSource and, therefore, provides internationalization (“i18n”) functionality. Spring also provides the HierarchicalMessageSource interface, which can resolve messages hierarchically. Together, these interfaces provide the foundation upon which Spring effects message resolution. The methods defined on these interfaces include:

1.15.1. 使用MessageSource国际化
ApplicationContext接口扩展了一个名为MessageSource的接口,
因此提供了国际化(“i18n”)功能。
Spring还提供了HierarchicalMessageSource接口,该接口可以分层解析消息。
这些接口一起提供了Spring实现消息解析的基础。
在这些接口上定义的方法包括:

  • String getMessage(String code, Object[] args, String default, Locale loc): The basic method used to retrieve a message from the MessageSource. When no message is found for the specified locale, the default message is used. Any arguments passed in become replacement values, using the MessageFormat functionality provided by the standard library.
  • String getMessage(String code, Object[] args, Locale loc): Essentially the same as the previous method but with one difference: No default message can be specified. If the message cannot be found, a NoSuchMessageException is thrown.
  • String getMessage(MessageSourceResolvable resolvable, Locale locale): All properties used in the preceding methods are also wrapped in a class named MessageSourceResolvable, which you can use with this method.
* String getMessage(String code, Object[] args, String default, Locale loc):
  用于从MessageSource检索消息的基本方法。
  如果未找到指定语言环境的message,则使用默认message。
  通过使用标准库提供的MessageFormat功能,传入的任何参数都将成为替换值。
* String getMessage(String code, Object[] args, Locale loc):本质上与前面的
  方法相同,但有一个区别:不能指定默认消息。
  如果找不到 message,则抛出NoSuchMessageException。
* String getMessage(MessageSourceResolvable, Locale Locale):
  在前面的方法中使用的所有属性也包装在一个名为MessageSourceResolvable类中,
  您可以在此方法中使用它。

When an ApplicationContext is loaded, it automatically searches for a MessageSource bean defined in the context. The bean must have the name messageSource. If such a bean is found, all calls to the preceding methods are delegated to the message source. If no message source is found, the ApplicationContext attempts to find a parent containing a bean with the same name. If it does, it uses that bean as the MessageSource. If the ApplicationContext cannot find any source for messages, an empty DelegatingMessageSource is instantiated in order to be able to accept calls to the methods defined above.

加载ApplicationContext时,它会自动搜索context中定义的MessageSource bean。
bean的名称必须是messageSource。
如果找到这样一个bean,对前面方法的所有调用都将委托给message资源。
如果没有找到message资源,ApplicationContext将尝试查找包含同名bean的父消息源。
如果是,则使用该bean作为消息源。
如果ApplicationContext找不到任何消息源,则实例化一个
空的DelegatingMessageSource,以便能够接受对上面定义的方法的调用。

Spring provides two MessageSource implementations, ResourceBundleMessageSource and StaticMessageSource. Both implement HierarchicalMessageSource in order to do nested messaging. The StaticMessageSource is rarely used but provides programmatic ways to add messages to the source. The following example shows ResourceBundleMessageSource:

Spring提供了两个消息源实现,ResourceBundleMessageSource和StaticMessageSource。
两者都实现了HierarchicalMessageSource以执行嵌套消息传递。
很少使用StaticMessageSource,但是提供了将messages添加到source的编程方法。
下面的示例显示了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>

The example assumes that you have three resource bundles called format, exceptions and windows defined in your classpath. Any request to resolve a message is handled in the JDK-standard way of resolving messages through ResourceBundle objects. For the purposes of the example, assume the contents of two of the above resource bundle files are as follows:

该示例假设在类路径中定义了三个资源包,分别称为format、exception和windows。
解析message的任何请求都以通过ResourceBundle对象解析消息的jdk标准方法处理。
对于示例,假设上述两个资源包文件的内容如下:

# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.

The next example shows a program to execute the MessageSource functionality. Remember that all ApplicationContext implementations are also MessageSource implementations and so can be cast to the MessageSource interface.

下一个示例显示了执行MessageSource功能的程序。
记住,所有ApplicationContext实现也是MessageSource实现,
因此可以cast到MessageSource接口。

public static void main(String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
    System.out.println(message);
}

The resulting output from the above program is as follows:

以上程序的结果输出如下:

Alligators rock!
To summarize, the MessageSource is defined in a file called beans.xml, which exists at the root of your classpath. The messageSource bean definition refers to a number of resource bundles through its basenames property. The three files that are passed in the list to the basenames property exist as files at the root of your classpath and are called format.properties, exceptions.properties, and windows.properties, respectively.

总之,MessageSource在一个名为bean.xml的文件中定义的,它存在于类路径的根目录。
messageSource bean definition通过其basenames属性引用许多资源包。
在列表中传递给basenames属性的三个文件作为类路径的根文件存在,分别称为format.properties,exceptions.properties, and windows.properties。

The next example shows arguments passed to the message lookup. These arguments are converted into String objects and inserted into placeholders in the lookup message.

下一个示例显示了传递给消息查找的参数。
这些参数被转换为字符串对象并插入到查找消息中的占位符中。

<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.something.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", Locale.ENGLISH);
        System.out.println(message);
    }
}

The resulting output from the invocation of the execute() method is as follows:

调用execute()方法的结果输出如下:

The userDao argument is required.

userDao参数是必需的。

With regard to internationalization (“i18n”), Spring’s various MessageSource implementations follow the same locale resolution and fallback rules as the standard JDK ResourceBundle. In short, and continuing with the example messageSource defined previously, if you want to resolve messages against the British (en-GB) locale, you would create files called format_en_GB.properties, exceptions_en_GB.properties, and windows_en_GB.properties, respectively.

关于国际化(“i18n”),Spring的各种MessageSource实现遵循与
标准JDK ResourceBundle相同的语言环境解析和回退规则。
简而言之,继续前面定义的示例messageSource,如果您希望根据
英国(en-GB)地区解析message,您将分别创建名为format_en_GB.properties, exceptions_en_GB.properties, and windows_en_GB.properties。

Typically, locale resolution is managed by the surrounding environment of the application. In the following example, the locale against which (British) messages are resolved is specified manually:

通常,语言环境解析由应用程序的周围环境管理。
在下面的示例中,手动指定解析(英国)消息所对应的语言环境:

#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);
}

The resulting output from the running of the above program is as follows:

运行上述程序的结果如下:

Ebagum lad, the ‘userDao’ argument is required, I say, required.

You can also use the MessageSourceAware interface to acquire a reference to any MessageSource that has been defined. Any bean that is defined in an ApplicationContext that implements the MessageSourceAware interface is injected with the application context’s MessageSource when the bean is created and configured.

您还可以使用MessageSourceAware接口来获取对已定义的任何消息源的引用。
当创建和配置bean时,在实现MessageSourceAware接口的
ApplicationContext中定义的任何bean都被注入应用程序
context的MessageSourceAware接口。

As an alternative to ResourceBundleMessageSource, Spring provides a ReloadableResourceBundleMessageSource class. This variant supports the same bundle file format but is more flexible than the standard JDK based ResourceBundleMessageSource implementation. In particular, it allows for reading files from any Spring resource location (not only from the classpath) and supports hot reloading of bundle property files (while efficiently caching them in between). See the ReloadableResourceBundleMessageSource javadoc for details.

作为ResourceBundleMessageSource的替代方案,Spring提供了一个ReloadableResourceBundleMessageSource类。
这个变体支持相同的bundle文件格式,但是比基于JDK的
标准ResourceBundleMessageSource实现更加灵活。
特别是,它允许从任何Spring资源位置读取文件(不仅仅是从类路径),
并支持bundle属性文件的热重新加载(同时有效地缓存它们)。
有关详细信息,请参见ReloadableResourceBundleMessageSource javadoc。

1.15.2. Standard and Custom Events

Event handling in the ApplicationContext is provided through the ApplicationEvent class and the ApplicationListener interface. If a bean that implements the ApplicationListener interface is deployed into the context, every time an ApplicationEvent gets published to the ApplicationContext, that bean is notified. Essentially, this is the standard Observer design pattern.

1.15.2. 标准和自定义事件
ApplicationContext中的事件处理是通过ApplicationEvent类和
ApplicationListener接口提供的。
如果实现ApplicationListener接口的bean被部署到上下文中,
那么每当一个ApplicationEvent被发布到ApplicationContext时,
该bean就会得到通知。
本质上,这就是标准的观察者设计模式。

As of Spring 4.2, the event infrastructure has been significantly improved and offers an annotation-based model as well as the ability to publish any arbitrary event (that is, an object that does not necessarily extend from ApplicationEvent). When such an object is published, we wrap it in an event for you.

从Spring 4.2开始,事件基础架构已经得到了显著改进,
并提供了一个基于注释的模型,以及发布任意事件的能力
(也就是说,不需要从ApplicationEvent扩展的对象)。
当发布这样的对象时,我们为您将其包装在事件中。

The following table describes the standard events that Spring provides:

下表描述了Spring提供的标准事件:

Table 7. Built-in Events
EventExplanation
ContextRefreshedEventPublished when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, “initialized” means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and the ApplicationContext object is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosen ApplicationContext actually supports such “hot” refreshes. For example, XmlWebApplicationContext supports hot refreshes, but GenericApplicationContext does not.
ContextStartedEventPublished when the ApplicationContext is started by using the start() method on the ConfigurableApplicationContext interface. Here, “started” means that all Lifecycle beans receive an explicit start signal. Typically, this signal is used to restart beans after an explicit stop, but it may also be used to start components that have not been configured for autostart (for example, components that have not already started on initialization).
ContextStoppedEventPublished when the ApplicationContext is stopped by using the stop() method on the ConfigurableApplicationContext interface. Here, “stopped” means that all Lifecycle beans receive an explicit stop signal. A stopped context may be restarted through a start() call.
ContextClosedEventPublished when the ApplicationContext is being closed by using the close() method on the ConfigurableApplicationContext interface or via a JVM shutdown hook. Here, “closed” means that all singleton beans will be destroyed. Once the context is closed, it reaches its end of life and cannot be refreshed or restarted.
RequestHandledEventA web-specific event telling all beans that an HTTP request has been serviced. This event is published after the request is complete. This event is only applicable to web applications that use Spring’s DispatcherServlet.
ServletRequestHandledEventA subclass of RequestHandledEvent that adds Servlet-specific context information.
EventExplanation
ContextRefreshedEvent当初始化或刷新ApplicationContext时发布(例如,通过使用ConfigurableApplicationContext接口上的refresh()方法)。在这里,“initialized”意味着加载所有bean,检测并激活后处理器bean,预实例化单例,并且ApplicationContext对象已经准备好可以使用了。只要上下文没有被关闭,刷新就可以被触发多次,前提是所选的ApplicationContext实际上支持这种“热”刷新。例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext不支持。
ContextStartedEvent通过使用ConfigurableApplicationContext接口上的start()方法启动ApplicationContext时发布。这里,“started”意味着所有生命周期bean都接收一个显式的启动信号。通常,这个信号用于在显式停止后重新启动bean,但它也可以用于启动尚未配置为自动启动的组件(例如,尚未在初始化时启动的组件).
ContextStoppedEvent在使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext时发布。这里,“stopped”意味着所有生命周期bean都接收一个显式的停止信号。可以通过调用start()重新启动已停止的上下文.
ContextClosedEvent通过使用ConfigurableApplicationContext接口上的close()方法或通过JVM关机hook关闭ApplicationContext时发布。在这里,“关闭”意味着所有单例bean将被销毁。一旦上下文关闭,它就会到达生命的终点,无法刷新或重新启动.
RequestHandledEvent一个特定于web的事件,告诉所有bean一个HTTP请求已经得到服务。此事件在请求完成后发布。此事件仅适用于使用Spring的DispatcherServlet的web应用程序。
ServletRequestHandledEventRequestHandledEvent的一个子类,用于添加特定于servlet的上下文信息。

You can also create and publish your own custom events. The following example shows a simple class that extends Spring’s ApplicationEvent base class:

您还可以创建和发布自己的自定义事件。下面的示例展示了一个简单的类,它扩展了Spring的ApplicationEvent基类:

public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlackListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}

To publish a custom ApplicationEvent, call the publishEvent() method on an ApplicationEventPublisher. Typically, this is done by creating a class that implements ApplicationEventPublisherAware and registering it as a Spring bean. The following example shows such a class:

要发布自定义ApplicationEvent,请调用ApplicationEventPublisher
上的publishEvent()方法。
通常,这是通过创建实现ApplicationEventPublisherAware的类
并将其注册为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 content) {
        if (blackList.contains(address)) {
            publisher.publishEvent(new BlackListEvent(this, address, content));
            return;
        }
        // send email...
    }
}

At configuration time, the Spring container detects that EmailService implements ApplicationEventPublisherAware and automatically calls setApplicationEventPublisher(). In reality, the parameter passed in is the Spring container itself. You are interacting with the application context through its ApplicationEventPublisher interface.

在配置时,Spring容器检测到EmailService实现了ApplicationEventPublisherAware,
并自动调用setApplicationEventPublisher()。
实际上,传入的参数就是Spring容器本身。您通过应用程序的
ApplicationEventPublisher接口与应用程序上下文交互。

To receive the custom ApplicationEvent, you can create a class that implements ApplicationListener and register it as a Spring bean. The following example shows such a class:

要接收自定义ApplicationEvent,您可以创建一个实现ApplicationListener的类,
并将其注册为一个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...
    }
}

Notice that ApplicationListener is generically parameterized with the type of your custom event (BlackListEvent in the preceding example). This means that the onApplicationEvent() method can remain type-safe, avoiding any need for downcasting. You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously. This means that the publishEvent() method blocks until all listeners have finished processing the event. One advantage of this synchronous and single-threaded approach is that, when a listener receives an event, it operates inside the transaction context of the publisher if a transaction context is available. If another strategy for event publication becomes necessary, see the javadoc for Spring’s ApplicationEventMulticaster interface and SimpleApplicationEventMulticaster implementation for configuration options.

注意,ApplicationListener通常是用自定义事件的类型
(前面示例中的BlackListEvent)参数化的。
这意味着onApplicationEvent()方法可以保持类型安全,
避免向下强制转换。
您可以注册任意数量的事件监听器,但是请注意,默认情况下,
事件监听器同步接收事件。
这意味着publishEvent()方法会阻塞,直到所有监听器都完成了事件的处理。
这种同步和单线程方法的一个优点是,当侦听器接收到事件时,
如果事务上下文可用,它将在发布程序的事务上下文内操作。
如果需要另一种事件发布策略,请参阅javadoc获取Spring的
ApplicationEventMulticaster接口和SimpleApplicationEventMulticaster
实现以获得配置选项。

The following example shows the bean definitions used to register and configure each of the classes above:

下面的示例显示了用于注册和配置上面每个类的bean定义:

<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>

Putting it all together, when the sendEmail() method of the emailService bean is called, if there are any email messages that should be blacklisted, a custom event of type BlackListEvent is published. The blackListNotifier bean is registered as an ApplicationListener and receives the BlackListEvent, at which point it can notify appropriate parties.

将它们放在一起,当调用emailService bean的sendEmail()方法时,
如果有任何应该被列入黑名单的电子邮件消息,则发布一个
BlackListEvent类型的自定义事件。
blackListNotifier bean注册为一个ApplicationListener并接收BlackListEvent,
此时它可以通知相关方。

Spring’s eventing mechanism is designed for simple communication between Spring beans within the same application context. However, for more sophisticated enterprise integration needs, the separately maintained Spring Integration project provides complete support for building lightweight, pattern-oriented, event-driven architectures that build upon the well-known Spring programming model.

Spring的事件机制是为相同应用程序上下文中的Spring bean之间的简单通信而设计的。
然而,对于更复杂的企业集成需求,单独维护的Spring integration项目提供了构建轻量级、
面向模式、事件驱动架构的完整支持,这些架构构建在众所周知的Spring编程模型之上。

Annotation-based Event Listeners

As of Spring 4.2, you can register an event listener on any public method of a managed bean by using the @EventListener annotation. The BlackListNotifier can be rewritten as follows:

基于注解的事件监听器
从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...
    }
}

The method signature once again declares the event type to which it listens, but, this time, with a flexible name and without implementing a specific listener interface. The event type can also be narrowed through generics as long as the actual event type resolves your generic parameter in its implementation hierarchy.

方法签名再次声明它侦听的事件类型,但是这次使用了灵活的名称,
并且没有实现特定的侦听器接口。
只要实际事件类型在其实现层次结构中解析泛型参数,就可以通过泛型缩小事件类型。

If your method should listen to several events or if you want to define it with no parameter at all, the event types can also be specified on the annotation itself. The following example shows how to do so:

如果您的方法应该侦听多个事件,或者您希望在不使用任何参数的情况下定义它,
那么还可以在注释本身上指定事件类型。
下面的例子展示了如何做到这一点:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    // ...
}

It is also possible to add additional runtime filtering by using the condition attribute of the annotation that defines a SpEL expression , which should match to actually invoke the method for a particular event.

还可以通过使用定义SpEL表达式的注释的条件属性来添加额外的运行时过滤,
该注释应该与针对特定事件实际调用方法相匹配。

The following example shows how our notifier can be rewritten to be invoked only if the content attribute of the event is equal to my-event:

下面的例子展示了如何重写通知程序,使其仅在事件的内容属性等于my-event时调用:

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlackListEvent(BlackListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}

Each SpEL expression evaluates against a dedicated context. The following table lists the items made available to the context so that you can use them for conditional event processing:

每个SpEL表达式根据一个专用上下文计算。
下表列出了可用的上下文项目,以便您可以使用它们进行条件事件处理:

Table 8. Event SpEL available metadata
NameLocationDescriptionExample
Eventroot objectThe actual ApplicationEvent.#root.event or event
Arguments arrayroot objectThe arguments (as an object array) used to invoke the method.#root.args or args; args[0] to access the first argument, etc.
Argument nameevaluation contextThe name of any of the method arguments. If, for some reason, the names are not available (for example, because there is no debug information in the compiled byte code), individual arguments are also available using the #a<#arg> syntax where <#arg> stands for the argument index (starting from 0).#blEvent or #a0 (you can also use #p0 or #p<#arg> parameter notation as an alias)

Note that #root.event gives you access to the underlying event, even if your method signature actually refers to an arbitrary object that was published.

请注意,#root.event允许您访问底层事件,
即使您的方法签名实际上引用了已发布的任意对象。

If you need to publish an event as the result of processing another event, you can change the method signature to return the event that should be published, as the following example shows:

如果你需要发布一个事件作为处理另一个事件的结果,
你可以改变方法签名来返回应该发布的事件,如下面的例子所示:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

This feature is not supported for asynchronous listeners.

异步监听器不支持此特性。

This new method publishes a new ListUpdateEvent for every BlackListEvent handled by the method above. If you need to publish several events, you can return a Collection of events instead.

这个新方法为上面方法处理的每个BlackListEvent发布一个新的ListUpdateEvent。
如果需要发布多个事件,则可以返回一个事件集合。

Asynchronous Listeners

If you want a particular listener to process events asynchronously, you can reuse the regular @Async support. The following example shows how to do so:

异步监听器
如果需要一个特定的侦听器异步处理事件,可以重用常规的@Async支持。
下面的例子展示了如何做到这一点:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

Be aware of the following limitations when using asynchronous events:

  • If an asynchronous event listener throws an Exception, it is not propagated to the caller. See AsyncUncaughtExceptionHandler for more details.
  • Asynchronous event listener methods cannot publish a subsequent event by returning a value. If you need to publish another event as the result of the processing, inject an ApplicationEventPublisher to publish the event manually.
在使用异步事件时要注意以下限制:
* 如果异步事件监听器抛出异常,它不会传播到调用者。
有关更多细节,请参阅 AsyncUncaughtExceptionHandler。
* 异步事件监听器方法不能通过返回值来发布后续事件。
如果您需要发布另一个事件作为处理的结果,
注入ApplicationEventPublisher来手动发布事件。

Ordering Listeners

If you need one listener to be invoked before another one, you can add the @Order annotation to the method declaration, as the following example shows:

监听器的排序
如果需要先调用一个监听器,可以在方法声明中添加@Order注释,如下例所示:

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}
Generic Events

You can also use generics to further define the structure of your event. Consider using an EntityCreatedEvent where T is the type of the actual entity that got created. For example, you can create the following listener definition to receive only EntityCreatedEvent for a Person:

通用的事件
还可以使用泛型进一步定义事件的结构。
考虑使用EntityCreatedEvent<T> 其中T是创建的实际实体的类型。
例如,您可以创建以下侦听器定义来只为Person类接收EntityCreatedEvent:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    // ...
}

Due to type erasure, this works only if the event that is fired resolves the generic parameters on which the event listener filters (that is, something like class PersonCreatedEvent extends EntityCreatedEvent {…}).

由于类型的消除,只有当触发的事件解析了事件侦听器过滤的泛型参数时
(即类似class PersonCreatedEvent extends EntityCreatedEvent<Person> {…})。

In certain circumstances, this may become quite tedious if all events follow the same structure (as should be the case for the event in the preceding example). In such a case, you can implement ResolvableTypeProvider to guide the framework beyond what the runtime environment provides. The following event shows how to do so:

在某些情况下,如果所有事件都遵循相同的结构(前面示例中的事件也应该如此),
那么这可能会变得非常冗长乏味。
在这种情况下,您可以实现ResolvableTypeProvider来指导运行时环境所提供的框架。
下面的事件展示了如何做到这一点:

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()));
    }
}

This works not only for ApplicationEvent but any arbitrary object that you send as an event.

这不仅适用于ApplicationEvent,也适用于任何作为事件发送的对象。

1.15.3. Convenient Access to Low-level Resources

For optimal usage and understanding of application contexts, you should familiarize yourself with Spring’s Resource abstraction, as described in Resources.

1.15.3. 便利地访问底层资源
为了优化使用和理解应用程序contexts,您应该熟悉Spring的资源抽象类,
如参考 Resources 章节。

An application context is a ResourceLoader, which can be used to load Resource objects. A Resource is essentially a more feature rich version of the JDK java.net.URL class. In fact, the implementations of the Resource wrap an instance of java.net.URL, where appropriate. A Resource can obtain low-level resources from almost any location in a transparent fashion, including from the classpath, a filesystem location, anywhere describable with a standard URL, and some other variations. If the resource location string is a simple path without any special prefixes, where those resources come from is specific and appropriate to the actual application context type.

应用程序 context 是一个ResourceLoader,可用于加载资源对象。
资源本质上是JDK java.net.URL类的功能丰富版本。
事实上,Resource的实现在适当的时候包装了一个java.net.URL的实例。
资源可以以透明的方式从几乎任何位置获取底层资源,
包括类路径、文件系统位置、可用标准URL描述的任何位置,以及其他一些变体。
如果resource位置字符串是没有任何特殊前缀的简单路径,
那么这些资源的来源是特定的,并且适合于实际的应用程序context类型。

You can configure a bean deployed into the application context to implement the special callback interface, ResourceLoaderAware, to be automatically called back at initialization time with the application context itself passed in as the ResourceLoader. You can also expose properties of type Resource, to be used to access static resources. They are injected into it like any other properties. You can specify those Resource properties as simple String paths and rely on automatic conversion from those text strings to actual Resource objects when the bean is deployed.

您可以配置部署到应用程序context中的bean,以实现特殊的回调接口
ResourceLoaderAware,在初始化时自动回调,而应用程序上下文本身
作为ResourceLoader传入。
您还可以公开Resource类型的属性,用于访问静态资源。
它们像其他属性一样被注入。
您可以将这些资源属性指定为简单的字符串路径,
并在部署bean时依赖于从这些文本字符串到实际资源对象的自动转换。

The location path or paths supplied to an ApplicationContext constructor are actually resource strings and, in simple form, are treated appropriately according to the specific context implementation. For example ClassPathXmlApplicationContext treats a simple location path as a classpath location. You can also use location paths (resource strings) with special prefixes to force loading of definitions from the classpath or a URL, regardless of the actual context type.

提供给ApplicationContext构造方法的位置路径或路径实际上是资源字符串,
并且以简单的形式,根据特定的context实现进行适当的处理。
例如,ClassPathXmlApplicationContext将简单的位置路径视为类路径位置。
您还可以使用带有特殊前缀的位置路径(资源字符串)来强制从类路径或URL加载定义,
而不管实际上下文类型是什么。

1.15.4. Convenient ApplicationContext Instantiation for Web Applications

1.15.4. 方便的ApplicationContext实例化Web应用程序

You can create ApplicationContext instances declaratively by using, for example, a ContextLoader. Of course, you can also create ApplicationContext instances programmatically by using one of the ApplicationContext implementations.

您可以通过使用ContextLoader来声明式地创建ApplicationContext实例。
当然,您也可以通过使用一个ApplicationContext实现来编程地
创建ApplicationContext实例。

You can register an ApplicationContext by using the ContextLoaderListener, as the following example shows:

你可以使用ContextLoaderListener注册一个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>

The listener inspects the contextConfigLocation parameter. If the parameter does not exist, the listener uses /WEB-INF/applicationContext.xml as a default. When the parameter does exist, the listener separates the String by using predefined delimiters (comma, semicolon, and whitespace) and uses the values as locations where application contexts are searched. Ant-style path patterns are supported as well. Examples are /WEB-INF/* Context.xml (for all files with names that end with Context.xml and that reside in the WEB-INF directory) and /WEB-INF/ ** /* Context.xml (for all such files in any subdirectory of WEB-INF).

侦听器检查contextConfigLocation参数。
如果该参数不存在,侦听器将使用/WEB-INF/applicationContext.xml作为默认值。
当参数确实存在时,侦听器将使用预定义的分隔符(逗号、分号和空格)分隔字符串,
并将这些值用作搜索应用程序上下文的位置。
也支持ant样式的路径模式。
例如/WEB-INF/* Context.xml
(用于所有名称以Context.xml结尾并位于WEB-INF目录中的文件)
和/WEB-INF/* * /* Context.xml
(用于WEB-INF的任何子目录中的所有此类文件)。

1.15.5. Deploying a Spring ApplicationContext as a Java EE RAR File

1.15.5. 将Spring ApplicationContext部署为Java EE RAR文件

It is possible to deploy a Spring ApplicationContext as a RAR file, encapsulating the context and all of its required bean classes and library JARs in a Java EE RAR deployment unit. This is the equivalent of bootstrapping a stand-alone ApplicationContext (only hosted in Java EE environment) being able to access the Java EE servers facilities. RAR deployment is a more natural alternative to a scenario of deploying a headless WAR file — in effect, a WAR file without any HTTP entry points that is used only for bootstrapping a Spring ApplicationContext in a Java EE environment.

可以将Spring ApplicationContext部署为RAR文件,
将上下文及其所有必需的bean类和库jar封装在Java EE RAR部署单元中。
这相当于引导一个独立的ApplicationContext(仅托管在Java EE环境中)
来访问Java EE服务器设施。
RAR部署是部署无头WAR文件的更自然的选择
—实际上是一个没有任何HTTP入口点的WAR文件,
仅用于在Java EE环境中引导Spring ApplicationContext。

RAR deployment is ideal for application contexts that do not need HTTP entry points but rather consist only of message endpoints and scheduled jobs. Beans in such a context can use application server resources such as the JTA transaction manager and JNDI-bound JDBC DataSource instances and JMS ConnectionFactory instances and can also register with the platform’s JMX server — all through Spring’s standard transaction management and JNDI and JMX support facilities. Application components can also interact with the application server’s JCA WorkManager through Spring’s TaskExecutor abstraction.

RAR部署对于不需要HTTP入口点,而只包含消息端点和计划作业的
应用程序上下文非常理想。
在这样的上下文中,bean可以使用应用服务器资源,比如JTA事务管理器、
JNDI绑定的JDBC数据源实例和JMS ConnectionFactory实例,
还可以注册平台的JMX服务器
——所有这些都通过Spring的标准事务管理和JNDI和JMX支持工具完成。
应用程序组件还可以通过Spring的TaskExecutor抽象与应用服务器的
JCA WorkManager交互。

See the javadoc of the SpringContextResourceAdapter class for the configuration details involved in RAR deployment.

有关RAR部署中涉及的配置细节,请参阅SpringContextResourceAdapter类的javadoc。

For a simple deployment of a Spring ApplicationContext as a Java EE RAR file:

对于一个简单的部署Spring ApplicationContext作为Java EE RAR文件:

  1. Package all application classes into a RAR file (which is a standard JAR file with a different file extension). .Add all required library JARs into the root of the RAR archive. .Add a META-INF/ra.xml deployment descriptor (as shown in the javadoc for SpringContextResourceAdapter) and the corresponding Spring XML bean definition file(s) (typically META-INF/applicationContext.xml).

  2. Drop the resulting RAR file into your application server’s deployment directory.

1. 所有应用程序的类打包成一个RAR文件(这是一个标准的JAR文件与不同的文件扩展名)。
增加所有必需的库JAR RAR存档的root。增加META-INF/ra.xml部署描述符
(如SpringContextResourceAdapter javadoc所示)和
相应的Spring XML bean definition文件(s)
(通常在META-INF/applicationContext.xml中)。
2. 将生成的RAR文件放到应用程序服务器的部署目录中。

Such RAR deployment units are usually self-contained. They do not expose components to the outside world, not even to other modules of the same application. Interaction with a RAR-based ApplicationContext usually occurs through JMS destinations that it shares with other modules. A RAR-based ApplicationContext may also, for example, schedule some jobs or react to new files in the file system (or the like). If it needs to allow synchronous access from the outside, it could (for example) export RMI endpoints, which may be used by other application modules on the same machine.

这样的RAR部署单元通常是自包含的。
它们不向外部世界公开组件,甚至不向同一应用程序的其他模块公开组件。
与基于 RAR 的ApplicationContext的交互通常是通过JMS destinations进行的,
JMS destinations与其他模块共享。
例如,基于RAR的ApplicationContext还可以调度一些作业或对文件系统中的
新文件(或类似的东西)作出反应。
如果它需要允许从外部进行同步访问,那么它可以(例如)导出RMI端点,
这可能被同一机器上的其他应用程序模块使用。

 类似资料:

相关阅读

相关文章

相关问答