ApplicationContext
中的事件处理是通过ApplicationEvent
类和ApplicationListener
接口提供的。如果将实现ApplicationListener
接口的bean
部署到上下文中,则每次将ApplicationEvent
发布到ApplicationContext
时,都会通知该bean
。本质上,这是标准的观察者模式实现。
从 Spring 4.2 开始,事件基础结构得到了显着改进,并提供了
annotation-based model
以及发布任何任意事件的功能,该对象不一定是从ApplicationEvent
扩展的。发布此类对象后,我们会为您包装一个事件。
Spring 自定义事件
您还可以创建和发布自己的自定义事件。这个例子演示了一个简单的类,它扩展了 Spring 的ApplicationEventBase
类:
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...
}
要发布自定义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...
}
}
在配置时,Spring 容器将检测到EmailService
实现ApplicationEventPublisherAware
并将自动调用setApplicationEventPublisher()
。实际上,传入的参数将是 Spring 容器本身。您只需通过其ApplicationEventPublisher
接口与应用程序上下文进行交互。
要接收自定义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...
}
}
请注意,ApplicationListener
通常使用自定义事件BlackListEvent
的类型进行参数化。这意味着onApplicationEvent()
方法可以保持类型安全,从而避免了向下转换的任何需要。您可以根据需要注册任意数量的事件监听器,但是请注意,默认情况下,事件监听器会同步接收事件。这意味着publishEvent()
方法将阻塞,直到所有侦听器都已完成对事件的处理为止。
这种同步和单线程方法的一个优点是,当侦听器收到事件时,如果有可用的事务上下文,它将在发布者的事务上下文内部进行操作。如果需要其他用于事件发布的策略,请参阅 Spring 的
ApplicationEventMulticaster
接口的 javadoc。
以下示例显示了用于注册和配置上述每个类的 Bean 定义:
<bean id="emailService" class="example.EmailService">
<property name="blackList">
<list>
<value>[emailprotected]</value>
<value>[emailprotected]</value>
<value>[emailprotected]</value>
</list>
</property>
</bean>
<bean id="blackListNotifier" class="example.BlackListNotifier">
<property name="notificationAddress" value="[emailprotected]"/>
</bean>
总而言之,当调用emailService
的sendEmail()
方法时,如果有任何电子邮件应被列入黑名单,则会发布BlackListEvent
类型的自定义事件。 blackListNotifier
被注册为ApplicationListener
并因此接收BlackListEvent
,此时它可以通知适当的参与者。
Spring 的事件机制旨在在同一应用程序上下文内在 Spring bean 之间进行简单的通信。但是,对于更复杂的企业集成需求,单独维护的Spring Integration项目为构建基于众所周知的 Spring 编程模型的轻量级事件
pattern-oriented
,事件驱动的体系结构提供了完整的支持。
基于注解的事件监听器
从 Spring 4.2 开始,可以通过EventListenerComments
在托管 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() {
...
}
也可以通过定义SpEL expression的注解的condition
属性添加其他运行时筛选,该SpEL expression
应该匹配以针对特定事件实际调用该方法。
例如,如果事件的content属性等于foo,则可以将我们的通知程序重写为仅被调用:
@EventListener(condition = "#blEvent.content == 'foo'")
public void processBlackListEvent(BlackListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
异步监听器(
asynchronous listeners
)不支持此功能。
监听器拓展
异步监听器
如果要特定的侦听器异步处理事件,只需重用常规@Async
支持:
@EventListener
@Async
public 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
是创建的实际实体的类型。您可以创建以下侦听器定义,以仅针对Person
接收EntityCreatedEvent
:
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
...
}
由于类型擦除,这仅在触发的事件解析了事件监听器所依据的通用参数(如class PersonCreatedEvent extends EntityCreatedEvent<Person> { … }
)时才有效。
在某些情况下,如果所有事件都遵循相同的结构,这可能会变得很乏味(就像上面的事件一样)。在这种情况下,您可以实现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()));
}
}
这不仅适用于
ApplicationEvent
,还适用于您作为事件发送的任何任意对象。
Spring 内置事件
Event | Explanation |
---|---|
ContextRefreshedEvent | 初始化或刷新ApplicationContext 时发布,例如,使用ConfigurableApplicationContext 接口上的refresh() 方法。这里的“已初始化”是指所有Bean 都已加载,检测到并激活了后处理器Bean ,已预先实例化单例,并且已准备好使用ApplicationContext 对象。只要尚未关闭上下文,只要选定的ApplicationContext 实际上支持这种“热”刷新,就可以多次触发刷新。例如,XmlWebApplicationContext 支持热刷新,但GenericApplicationContext 不支持。 |
ContextStartedEvent | 在ApplicationContext 启动时使用ConfigurableApplicationContext 界面上的start() 方法发布。这里的“已启动”表示所有Lifecycle bean 都收到一个明确的启动signal 。通常,此signal 用于在显式停止后重新启动 Bean ,但也可以用于启动尚未配置为自动启动的组件,例如,尚未在初始化时启动的组件。 |
ContextStoppedEvent | 在ApplicationContext 停止时使用ConfigurableApplicationContext 界面上的stop() 方法发布。这里的“停止”表示所有Lifecycle bean 都收到一个明确的停止signal 。停止的上下文可以通过start() 调用重新启动。 |
ContextClosedEvent | 在ApplicationContext 关闭时使用ConfigurableApplicationContext 界面上的close() 方法发布。这里的“关闭”表示所有单例bean 都被销毁。封闭的环境已经到了生命的尽头。它不能刷新或重新启动。 |
RequestHandledEvent | 一个特定于 Web 的事件,告诉所有 Bean HTTP 请求已得到服务。该事件在请求完成后发布。此事件仅适用于使用Spring 的DispatcherServlet 的 Web 应用程序。 |
ServletRequestHandledEvent | RequestHandledEvent 的子类,用以添加Servlet 特定的上下文信息 |
参考资料: