Spring 4.2+ 中注解驱动的事件监听器

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 63w+ 字,讲解图 2808+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2200+ 小伙伴加入学习 ,欢迎点击围观

在应用程序内交换事件已成为许多应用程序不可或缺的一部分,值得庆幸的是 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 版本——它应该适用于大多数情况。

相关文章