【注】本文译自:Spring Events | Baeldung
1. 概述
在本教程中,咱们将探讨如何在 Spring 中应用事件。
事件是框架中最容易被忽视的性能之一,但也是更有用的性能之一。和 Spring 中的许多其余货色一样,事件公布是 ApplicationContext 提供的性能之一。
有一些简略的指导方针能够遵循:
- 如果咱们应用 Spring Framework 4.2 之前的版本,事件类应该扩大 ApplicationEvent。从 4.2 版本开始,事件类不再须要扩大 ApplicationEvent 类。
- 发布者应该注入一个 ApplicationEventPublisher 对象。
监听器应实现 ApplicationListener 接口。
2.自定义事件
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:
@Componentpublic class CustomSpringEventPublisher { @Autowired private ApplicationEventPublisher applicationEventPublisher; public void publishCustomEvent(final String message) { System.out.println("Publishing custom event. "); CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message); applicationEventPublisher.publishEvent(customSpringEvent); }}
或者,发布者类能够实现
ApplicationEventPublisherAware 接口,这也会在应用程序启动时注入事件发布者。通常,将 @Autowire 注入发布者会更简略。
从 Spring Framework 4.2 开始,ApplicationEventPublisher 接口为 publishEvent(Object event) 办法提供了一个新的重载,该办法承受任何对象作为事件。因而,Spring 事件不再须要扩大 ApplicationEvent 类。
2.3. 监听器
最初,让咱们创立监听器。
监听器的惟一要求是是一个 bean 并实现 ApplicationListener 接口:
@Componentpublic 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 很好地实现了这个目标:
@Configurationpublic 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 的任何 public 办法上注册:
@Componentpublic class AnnotationDrivenEventListener { @EventListener public void handleContextStart(ContextStartedEvent cse) { System.out.println("Handling context started event."); }}
和以前一样,办法签名申明了它应用的事件类型。
默认状况下,监听器是同步调用的。然而,咱们能够通过增加 @Async 正文轻松地使其异步。咱们只须要记住在应用程序中启用异步反对。
6.泛型反对
也能够应用事件类型中的泛型信息来调度事件。
6.1. 泛型应用程序事件
让咱们创立一个泛型事件类型。
在咱们的示例中,事件类蕴含任何内容和 success 状态指示器:
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 接口来定义监听器:
@Componentpublic class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> { @Override public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) { System.out.println("Received spring generic event - " + event.getWhat()); }}
但可怜的是,这个定义要求咱们从 ApplicationEvent 类继承 GenericSpringEvent。因而,对于本教程,让咱们应用之前探讨过的正文驱动事件监听器。
通过在 @EventListener 正文上定义布尔 SpEL 表达式,也能够使事件监听器有条件。
在这种状况下,只有胜利调用 GenericSpringEvent 的 String 对象时才会调用事件处理程序:
@Componentpublic class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> { @Override public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) { System.out.println("Received spring generic event - " + event.getWhat()); }
}
Spring 表达式语言 (SpEL)") 是一种弱小的表达式语言,在另一篇教程中有具体介绍。
6.3. 发布者
事件发布者与上述相似。然而因为类型擦除,咱们须要公布一个事件来解析咱们将过滤的泛型参数,例如,类 GenericStringSpringEvent extends 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 的我的项目,因而它应该很容易导入和运行。