在应用程序内交换事件已成为许多应用程序不可或缺的一部分,值得庆幸的是 Spring 为瞬态事件 (*) 提供了完整的基础设施。最近对事务绑定事件的重构给了我一个借口来检查和实践 Spring 4.2 中引入的新的注解驱动的事件侦听器。让我们看看可以得到什么。
(*) – 对于基于 Spring 的应用程序中的持久事件, Duramen 可能是一个值得一看的解决方案。
老办法
要获得有关事件(包括 Spring 事件和自定义域事件)的通知,必须创建一个使用
onApplicationEvent
实现
ApplicationListener
的组件。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
它工作正常,但对于 每个 事件都必须创建一个新类来生成样板代码。
此外,我们的事件
必须
扩展
ApplicationEvent
类——Spring 中所有应用程序事件的基类。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
请注意 ,在事件中使用域对象有明显的缺点,在许多情况下并不是最好的主意。使用代码示例中的伪域对象是为了避免引入不必要的复杂性。
顺便说一句,本例中的
ExternalNotificationSender
是一个类的实例,它向注册用户发送外部通知(例如通过电子邮件、SMS 或 Slack)。
注解驱动的事件监听器
从 Spring 4.2 开始,要收到有关新事件的通知,在任何 Spring 组件中使用
@EventListener
注释来注释方法就足够了。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
在底层,Spring 将为事件创建一个
ApplicationListener
实例,其类型取自方法参数。一个类中注释方法的数量没有限制——所有相关的事件处理程序都可以归为一个类。
条件事件处理
为了使
@EventListener
更加有趣,它能够仅处理那些满足用
SpEL
编写的给定条件的给定类型的事件。让我们假设以下事件类:
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
请注意,在实际应用程序中,可能会有一个与博客相关的事件层次结构。
另请注意,在 Groovy 中,该类会简单得多。
要仅为重要更改生成事件,可以使用
condition
参数:
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
宽松的事件类型层次结构
历史上,
ApplicationEventPublisher
只能发布在 ApplicationEvent 之后继承的对象。从 Spring 4.2 开始,接口已扩展为支持任何对象类型。在那种情况下,对象被包装在
PayloadApplicationEvent
中并通过发送。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
这一变化使发布事件变得更加容易。然而,另一方面,如果没有内在的责任心(例如,我们所有领域事件的标记接口),它会使事件跟踪变得更加困难,尤其是在大型应用程序中。
发布事件响应
@EventListener
的另一个好处是,在非 void 返回类型的情况下,Spring 将自动发布返回的事件。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
异步事件处理
正如
Radek Grębski
正确建议的那样,还值得一提的是,
@EventListener
可以轻松地与
@Async
注释结合使用以提供异步事件处理。特定事件侦听器中的代码既不会阻止主代码的执行,也不会阻止其他侦听器的处理。
@Component
class OldWayBlogModifiedEventListener implements
ApplicationListener<OldWayBlogModifiedEvent> {
(...)
@Override
public void onApplicationEvent(OldWayBlogModifiedEvent event) {
externalNotificationSender.oldWayBlogModified(event);
}
}
要使其工作,只需要在您的 Spring 上下文/应用程序中使用
@EnableAsync
启用异步方法执行。
概括
Spring 4.2 中引入的注解驱动的事件侦听器延续了减少基于 Spring (Boot) 的应用程序中样板代码的趋势。这种新方法看起来很有趣,特别是对于具有少量事件且维护开销较低的小型应用程序。在无处不在的 Spring (Boot) 魔法世界中,更值得记住的是,能力越大,责任越大。
在下一篇博文中,我将介绍如何使用新机制来简化事务绑定事件的处理。
请注意 ,Spring Framework 4.2 是 Spring Boot 1.3 的默认依赖项(在撰写本文时,1.3.0.M5 可用)。或者,可以在 Gradle/Maven 中为 Spring Boot 1.2.5 手动升级 Spring Framework 版本——它应该适用于大多数情况。