Spring Events
1. 概述
在本文中,我们将讨论如何在 Spring 框架中使用事件(Events)。
事件是 Spring 框架中最容易被忽略的功能之一,但同时也是最有用的功能之一。与 Spring 中的许多其他机制一样,事件发布是 ApplicationContext 提供的核心功能之一。
使用 Spring 事件时,有一些简单的准则可以遵循:
- 事件类应扩展
ApplicationEvent - 发布者应注入一个
ApplicationEventPublisher对象 - 监听器应实现
ApplicationListener接口
2. 自定义事件 (A Custom Event)
Spring 允许创建和发布自定义事件,这些事件默认情况下是同步处理的。这具有一些优点,例如监听器能够参与发布者的事务环境。
2.1 简单应用程序事件
让我们创建一个简单的事件类,它主要作为一个占位符来存储事件数据。在这种情况下,事件类包含一个 String 类型的消息:
public class CustomSpringEvent extends ApplicationEvent {
private String message;
public CustomSpringEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}2.2 事件发布者
现在,让我们创建该事件的发布者。发布者负责构造事件对象并将其发布给任何正在监听的组件。
要发布事件,发布者可以简单地注入 ApplicationEventPublisher 并使用 publishEvent() API:
@Component
public class CustomSpringEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void doStuffAndPublishAnEvent(final String message) {
System.out.println("Publishing custom event. ");
CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
applicationEventPublisher.publishEvent(customSpringEvent);
}
}或者,发布者类可以实现 ApplicationEventPublisherAware 接口,这将在应用程序启动时注入事件发布者。不过通常来说,使用 @Autowired 注入发布者会更加简单。
2.3 事件监听器
最后,让我们创建监听器。
监听器的唯一要求是成为一个 Bean 并实现 ApplicationListener 接口:
@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
@Override
public void onApplicationEvent(CustomSpringEvent event) {
System.out.println("Received spring custom event - " + event.getMessage());
}
}注意,我们的自定义监听器是通过自定义事件的泛型类型进行参数化的——这使得 onApplicationEvent() 方法具有类型安全性。这也避免了必须检查对象是否是特定事件类的实例并进行强制转换。
并且,正如前面所讨论的(默认情况下,Spring 事件是同步的),doStuffAndPublishAnEvent() 方法将阻塞,直到所有监听器完成对事件的处理为止。
3. 创建异步事件
在某些情况下,同步发布事件并不是我们真正想要的,我们可能需要异步处理事件。
您可以通过在配置中创建 ApplicationEventMulticaster Bean 并指定执行器来启用异步支持。出于我们的目的,SimpleAsyncTaskExecutor 是一个不错的选择:
@Configuration
public class AsynchronousSpringEventsConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster =
new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
}事件、发布者和监听器的实现与以前相同——但是现在,监听器将在单独的线程中异步处理事件。
4. 现有框架事件
Spring 本身可以发布各种事件。例如,ApplicationContext 将触发各种框架事件,如 ContextRefreshedEvent、ContextStartedEvent、RequestHandledEvent 等。
这些事件为应用程序开发人员提供了一个选项,可以挂接到应用程序和上下文的生命周期中,并在需要时添加自己的自定义逻辑。
这是一个监听上下文刷新事件的快速示例:
public class ContextRefreshedListener
implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent cse) {
System.out.println("Handling context re-freshed event. ");
}
}要了解有关现有框架事件的更多信息,请查看我们的下一个教程。
5. 注释驱动的事件监听器
从 Spring 4.2 开始,事件监听器不必是实现 ApplicationListener 接口的 Bean,可以通过 @EventListener 注解在托管 Bean 的任何公共方法上注册它:
@Component
public class AnnotationDrivenContextStartedListener {
// @Async
@EventListener
public void handleContextStart(ContextStartedEvent cse) {
System.out.println("Handling context started event.");
}
}和以前一样,方法签名声明其使用的事件类型。此监听器默认被同步调用。但是现在使其异步就像添加 @Async 注解一样简单(不要忘记在应用程序中启用 Async 支持)。
6. 泛型支持
还可以使用事件类型中的泛型信息来调度事件。
6.1 通用应用程序事件
让我们创建一个通用事件类型。在我们的示例中,事件类包含任意内容和成功状态指示符:
public class GenericSpringEvent<T> {
private T what;
protected boolean success;
public GenericSpringEvent(T what, boolean success) {
this.what = what;
this.success = success;
}
// ... standard getters
}注意 GenericSpringEvent 和 CustomSpringEvent 之间的区别。现在,我们可以灵活地发布任何任意事件,并且不再需要从 ApplicationEvent 扩展它。
6.2 监听器
现在,让我们创建该事件的监听器。我们可以像以前一样通过实现 ApplicationListener 接口来定义监听器:
@Component
public class GenericSpringEventListener
implements ApplicationListener<GenericSpringEvent<String>> {
@Override
public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) {
System.out.println("Received spring generic event - " + event.getWhat());
}
}但不幸的是,此定义要求 GenericSpringEvent 从 ApplicationEvent 类继承。因此,对于本教程,让我们利用前面讨论的注释驱动的事件监听器。
通过在 @EventListener 注解上定义布尔 SpEL 表达式,也可以使事件监听器具有条件性。在这种情况下,事件处理器仅会在成功的 GenericSpringEvent<String> 时被调用:
@Component
public class AnnotationDrivenEventListener {
@EventListener(condition = "#event.success")
public void handleSuccessful(GenericSpringEvent<String> event) {
System.out.println("Handling generic event (conditional).");
}
}Spring 表达式语言 (SpEL) 是一种强大的表达式语言,将在另一个教程中详细介绍。
6.3 事件发布者
事件发布类似于前述方式。但是由于类型擦除,我们需要发布一个事件来解析将要过滤的泛型参数。例如,类 GenericStringSpringEvent 扩展了 GenericSpringEvent<String>。
还有一种发布事件的替代方法。如果我们从带有 @EventListener 注解的方法中返回非空值作为结果,Spring Framework 将为我们发送该结果作为新事件。此外,作为事件处理的结果,我们可以通过将它们返回到集合中来发布多个新事件。
7. 事务绑定事件
本段是关于使用 @TransactionalEventListener 注解的。要了解有关事务管理的更多信息,请查看"Spring 事务和 JPA"教程。
从 Spring 4.2 开始,该框架提供了一个新的 @TransactionalEventListener 注解,它是 @EventListener 的扩展,允许将事件的监听器绑定到事务的某个阶段。可以绑定到以下事务阶段:
AFTER_COMMIT(默认值):如果事务成功完成,则触发事件AFTER_ROLLBACK:如果事务已回滚AFTER_COMPLETION:如果事务已完成(AFTER_COMMIT和AFTER_ROLLBACK的别名)BEFORE_COMMIT:用于在事务提交之前立即触发事件
这是事务性事件监听器的快速示例:
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCustom(CustomSpringEvent event) {
System.out.println("Handling event inside a transaction BEFORE COMMIT.");
}仅当存在事件生成器正在运行且即将提交的事务时,才会调用此监听器。
而且,如果没有事务在运行,则除非我们通过将 fallbackExecution 属性设置为 true 来覆盖此行为,否则根本不会发送该事件。
8. 总结
在本快速教程中,我们介绍了在 Spring 中处理事件的基础知识——创建一个简单的自定义事件,将其发布,然后在监听器中进行处理。
我们还简要介绍了如何在配置中启用事件的异步处理。
然后,我们了解了 Spring 4.2 中引入的改进,例如注释驱动的监听器、更好的泛型支持以及绑定到事务阶段的事件。
与往常一样,可以在 Github 上获得本文中提供的代码。这是一个基于 Maven 的项目,因此应该很容易直接导入和运行。
说明:本文涉及的部分特性(如@EventListener、@TransactionalEventListener)自 Spring 4.2 起引入。如果您使用的是更高版本的 Spring(如 Spring 5.x 或 6.x),这些功能依然适用且可能包含进一步的优化。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/spring-events.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。