SpringBoot事件机制相关知识点汇总

要“监听”事件,我们总是可以将“监听器”作为事件源中的另一个方法写入事件,但这将使事件源与监听器的逻辑紧密耦合。

对于实际事件,我们比直接方法调用更灵活。我们可以根据需要动态注册和注销某些事件的侦听器。我们还可以为同一事件设置多个侦听器。

本教程概述了如何发布和侦听自定义事件,并解释了 Spring Boot 的内置事件。

为什么我应该使用事件而不是直接方法调用?

事件和直接方法调用都适合于不同的情况。使用方法调用,就像断言一样-无论发送和接收模块的状态如何,他们都需要知道此事件的发生。

对于事件,另一方面,我们只知道发生了一个事件,哪些模块会被通知并不是我们关心的问题。当我们想要将某些业务处理传递给另一个线程时(例如:在某些任务完成时发送电子邮件),最好使用事件。此外,事件对于测试驱动的开发也很有用。

什么是应用程序事件( Application Events)?

Spring 应用程序事件允许我们发送和接收特定应用程序事件,我们可以根据需要处理这些事件。事件用于在松散耦合的组件之间交换信息。由于发布者和订阅者之间没有直接耦合,因此可以在不影响发布者的情况下修改订阅者,反之亦然。

让我们看看如何在 Spring Boot 应用程序中创建、发布和侦听自定义事件。

创建ApplicationEvent

我们可以使用 Spring Framework 的事件发布机制发布应用程序事件。

让我们通过扩展来创建调用的自定义事件:

class UserCreatedEvent extends ApplicationEvent {
 private String name;
 UserCreatedEvent(Object source, String name) {
  super(source);
  this.name = name;
 }
 ...
}

代码中super(source)中的source应该是最初发生事件的对象或与事件相关联的对象。

从Spring 4.2开始,我们还可以将对象发布为事件,而无需扩展ApplicationEvent:

class UserRemovedEvent {
 private String name;

 UserRemovedEvent(String name) {
  this.name = name;
 }
 ...
}

发布一个ApplicationEvent

我们使用ApplicationEventPublisher接口发布事件:

@Component
class Publisher {

 private final ApplicationEventPublisher publisher;

  Publisher(ApplicationEventPublisher publisher) {
   this.publisher = publisher;
  }
 void publishEvent(final String name) {
  // Publishing event created by extending ApplicationEvent
  publisher.publishEvent(new UserCreatedEvent(this, name));
  // Publishing an object as an event
  publisher.publishEvent(new UserRemovedEvent(name));
 }
}

当我们发布的对象不是ApplicationEvent时,Spring会自动为我们将其包装在PayloadApplicationEvent中。

接收应用程序事件

现在,我们知道如何创建和发布自定义事件,让我们看看如何侦听该事件。事件可以有多个侦听器并且根据应用程序要求执行不同的工作。

有两种方法可以定义侦听器。我们可以使用注解(@EventListener)或实现接口(ApplicationListener)。在这两种情况下,侦听器类都必须由 Spring 管理。

注解

从Spring 4.1开始,可以使用@EventListener注解的方法,以自动注册与该方法签名匹配的ApplicationListener:

@Component
class UserRemovedListener {

 @EventListener
 ReturnedEvent handleUserRemovedEvent(UserRemovedEvent event) {
  // handle UserRemovedEvent ...
  return new ReturnedEvent();
 }

 @EventListener
 void handleReturnedEvent(ReturnedEvent event) {
    // handle ReturnedEvent ...
 }
 ...
}

启用注解驱动的配置时,不需要其他配置。我们的方法可以监听多个事件,或者如果我们想完全不使用任何参数来定义它,那么事件类型也可以在注解本身上指定。示例:@EventListener({ContextStartedEvent.class,ContextRefreshedEvent.class})。

对于使用@EventListener注解并定义为具有返回类型的方法,Spring会将结果作为新事件发布给我们。在上面的示例中,第一个方法返回的ReturnedEvent将被发布,然后由第二个方法处理。

如果指定SpEL条件,Spring仅在某些情况下才允许触发我们的侦听器:

@Component
class UserRemovedListener {
 @EventListener(condition = "#event.name eq 'reflectoring'")
 void handleConditionalListener(UserRemovedEvent event) {
  // handle UserRemovedEvent
 }
}

仅当表达式的计算结果为true或以下字符串之一时才处理该事件:“ true”,“ on”,“ yes”或“ 1”。方法参数通过其名称公开。条件表达式还公开了一个“ root”变量,该变量引用原始ApplicationEvent(#root.event)和实际方法参数(#root.args)

在以上示例中,仅当#event.name的值为'reflectoring'时,才会使用UserRemovedEvent触发监听器。

实现ApplicationListener接口

侦听事件的另一种方法是实现ApplicationListener接口:

@Component
class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {
 @Override
 public void onApplicationEvent(UserCreatedEvent event) {
  // handle UserCreatedEvent
 }
}

只要侦听器对象在Spring应用程序上下文中注册,它就会接收事件。当Spring路由一个事件时,它使用侦听器的签名来确定它是否与事件匹配。

异步事件侦听器

默认情况下,spring事件是同步的,这意味着发布者线程将阻塞,直到所有侦听器都完成对事件的处理为止。

要使事件侦听器以异步模式运行,我们要做的就是在该侦听器上使用@Async注解:

@Component
class AsyncListener {
 @Async
 @EventListener
 void handleAsyncEvent(String event) {
  // handle event
 }
}

为了使@Async注解起作用,我们还必须使用@EnableAsync注解我们的@Configuration类之一或@SpringBootApplication类。

上面的代码示例还显示了我们可以将String用作事件。使用风险自负。最好使用特定于我们用例的数据类型,以免与其他事件冲突。

Transaction-绑定事件

Spring允许我们将事件侦听器绑定到当前事务的某个阶段。如果当前事务的结果对侦听器很重要时,这使事件可以更灵活地使用。

当我们使用@TransactionalEventListener注释方法时,我们将获得一个扩展的事件侦听器,该侦听器可以了解事务:

@Component
class UserRemovedListener {
 @TransactionalEventListener(phase=TransactionPhase.AFTER_COMPLETION)
 void handleAfterUserRemoved(UserRemovedEvent event) {
  // handle UserRemovedEvent
 }
}

仅当当前事务完成时才调用UserRemovedListener。

我们可以将侦听器绑定到事务的以下阶段:

AFTER_COMMIT:事务成功提交后,将处理该事件。如果事件侦听器仅在当前事务成功时才运行,则可以使用此方法。

AFTER_COMPLETION:事务提交或回滚时将处理该事件。例如,我们可以使用它在事务完成后执行清理。

AFTER_ROLLBACK:事务回滚后将处理该事件。

BEFORE_COMMIT:该事件将在事务提交之前进行处理。例如,我们可以使用它来将事务性ORM会话刷新到数据库。

Spring Boot的 Application Events

Spring Boot提供了几个与SpringApplication生命周期相关的预定义ApplicationEvent。

在创建ApplicationContext之前会触发一些事件,因此我们无法将这些事件注册为@Bean。我们可以通过手动添加侦听器来注册这些事件的侦听器:

@SpringBootApplication
public class EventsDemoApplication {

 public static void main(String[] args) {
  SpringApplication springApplication =
    new SpringApplication(EventsDemoApplication.class);
  springApplication.addListeners(new SpringBuiltInEventsListener());
  springApplication.run(args);
 }
}

通过将META-INF/spring.factories文件添加到我们的项目中,我们还可以注册侦听器,而不管如何创建应用程序,并使用

org.springframework.context.ApplicationListener键引用侦听器:

org.springframework.context.ApplicationListener= com.reflectoring.eventdemo.SpringBuiltInEventsListener

class SpringBuiltInEventsListener
  implements ApplicationListener<SpringApplicationEvent>{

 @Override
 public void onApplicationEvent(SpringApplicationEvent event) {
  // handle event
 }
}

确定事件监听器已正确注册后,便可以监听所有Spring Boot的SpringApplicationEvents。让我们按照它们在应用程序启动过程中的执行顺序来进行观察。

ApplicationStartingEvent

除了运行侦听器和初始化程序的注册之外,ApplicationStartingEvent在运行开始时但在任何处理之前都会触发。

ApplicationEnvironmentPreparedEvent

当上下文中使用的环境可用时,将触发ApplicationEnvironmentPreparedEvent。

由于此时环境已准备就绪,因此我们可以在其他Bean使用它之前对其进行检查和修改。

ApplicationContextInitializedEvent

当ApplicationContext准备就绪并且调用ApplicationContextInitializers但尚未加载bean定义时,将触发ApplicationContextInitializedEvent。

在bean初始化到Spring容器之前,我们可以使用它来执行任务。

ApplicationPreparedEvent

准备好ApllicationContext但未刷新时会触发ApplicationPreparedEvent。

该环境已准备就绪,可以使用,并且将加载Bean定义。

WebServerInitializedEvent

如果我们使用的是网络服务器,则在网络服务器准备就绪后会触发WebServerInitializedEvent。 ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是servlet和反应式网络服务。

WebServerInitializedEvent不扩展SpringApplicationEvent。

ApplicationStartedEvent

在刷新上下文之后但在调用任何应用程序和命令行运行程序之前,将触发ApplicationStartedEvent。

ApplicationReadyEvent

触发ApplicationReadyEvent来指示该应用程序已准备就绪,可以处理请求。

建议此时不要修改内部状态,因为所有初始化步骤都将完成。

ApplicationFailedEvent

如果存在异常并且应用程序无法启动,则会触发ApplicationFailedEvent。在启动期间的任何时间都可能发生这种情况。

我们可以使用它来执行一些任务,例如执行脚本或在启动失败时发出通知。

结论

事件是为在同一应用程序上下文内的Spring Bean之间进行简单通信而设计的。从Spring 4.2开始,基础结构已得到显着改进,并提供了基于注释的模型以及发布任意事件的功能。

英文原文:https://reflectoring.io/spring-boot-application-events-explained/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解SpringBoot 发布ApplicationEventPublisher和监听ApplicationEvent事件

    资料地址 Spring @Aync 实现方法 自定义需要发布的事件类,需要继承ApplicationEvent类或PayloadApplicationEvent<T>(该类也仅仅是对ApplicationEvent的一层封装) 使用@EventListener来监听事件 使用ApplicationEventPublisher来发布自定义事件(@Autowired注入即可) /** * 自定义保存事件 * @author peter * 2019/1/27 14:59 */ public cla

  • 浅谈Spring-boot事件监听

    springboot的事件监听:为bean之间的消息通信提供支持.当一个bean做完一件事以后,通知另一个bean知晓并做出相应处理.这时,我们需要另一个bean,监听当前bean所发生的事件. 实现步骤:四个步骤,四种方式 第一种方式 1.自定义事件,一般是继承ApplicationEvent抽象类 2.定义事件监听器,一般是实现ApplicationListener接口 3.1)把监听器加入到SpringApplication中:ApplicationListener.addListener

  • SpringBoot Application事件监听的实现方案

    先说结论 SpringBoot Application共支持6种事件监听,按顺序分别是: ApplicationStartingEvent:在Spring最开始启动的时候触发 ApplicationEnvironmentPreparedEvent:在Spring已经准备好上下文但是上下文尚未创建的时候触发 ApplicationPreparedEvent:在Bean定义加载之后.刷新上下文之前触发 ApplicationStartedEvent:在刷新上下文之后.调用application命令之

  • Spring Boot应用事件监听示例详解

    前言 本文主要给大家介绍了关于Spring Boot应用事件监听的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 1. Spring Boot特有的应用事件 除了Spring框架的事件,Spring Boot的SpringApplication也发送了一些自己的事件: ApplicationStartingEvent:在任何处理(除了注册listener和initializer)开始之前发送. ApplicationEnvironmentPreparedEvent: 在

  • springBoot的事件机制GenericApplicationListener用法解析

    什么是ApplicationContext? 它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些. ApplicationContext则是应用的容器. Spring把Bean(object)放在容器中,需要用就通过get方法取出来. ApplicationEvent 是个抽象类,里面只有一个构造函数和一个长整型的timestamp. springboot的event的类型: ApplicationStartingEvent ApplicationEnviro

  • springboot关于容器启动事件总结

    在springboot 容器启动时,我们需要在启动过程中做一些操作,比如启动容器后,执行某些代码. spring 提供了监听器,我们可以方便的实现这些操作. 在容器启动开始时: package com.neo.filter; import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.context.ApplicationListener; public cl

  • springboot 事件监听的实现方法

    定义事件 @Getter public class TestEvent extends ApplicationEvent { private String msg; public TestEvent(Object source, String msg) { super(source); this.msg = msg; } } 定义事件监听(注解方式) @Component public class TestListen { @EventListener public void testListe

  • spring boot之SpringApplication 事件监听

    spring application listener 在 spring 框架中,有多种事件, 这些时间会在不同的运行时刻发布,来通知监听者.本文仅仅介绍 SpringApplicationEvent 的事件的监听. 事件类型 EventType 发布时间 ApplicationContextInitializedEvent 在 SpringApplication正在启动, ApplicationContext 已经准备好了,ApplicationContextInitializers 被调用,

  • springboot+redis过期事件监听实现过程解析

    1 修改 redis.conf配置文件: K Keyspace events, published with keyspace@ prefix事件 E Keyevent events, published with keyevent@ prefix g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, - $ String commands l List commands s Set commands h Hash co

  • SpringBoot事件机制相关知识点汇总

    要"监听"事件,我们总是可以将"监听器"作为事件源中的另一个方法写入事件,但这将使事件源与监听器的逻辑紧密耦合. 对于实际事件,我们比直接方法调用更灵活.我们可以根据需要动态注册和注销某些事件的侦听器.我们还可以为同一事件设置多个侦听器. 本教程概述了如何发布和侦听自定义事件,并解释了 Spring Boot 的内置事件. 为什么我应该使用事件而不是直接方法调用? 事件和直接方法调用都适合于不同的情况.使用方法调用,就像断言一样-无论发送和接收模块的状态如何,他们都

  • SpringBoot整合Mybatis的知识点汇总

    springboots使用的版本是2.0.1,注意不同版本可能有差异,并不一定通用 添加Mybatis的起步依赖: <!--mybatis起步依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version&

  • jQuery中ajax的相关知识点汇总

    前言 学习JavaScript的同学都知道, AJAX (async javascript and xml)翻译叫做异步的JavaScript和XML , 在原生js中使用发送网络请求也是一件麻烦事,每次都是那几个步骤. 我们先来回顾一下在原生js中如何发送一个 ajax 网络请求 经典4步曲 1.原生js的ajax网络请求 // IE9及以上 // const xhr = new XMLHttpRequest() // IE9以下 // const xhr = new ActiveXObjec

  • Spring的事件机制知识点详解及实例分析

    同步事件和异步事件 同步事件: 在一个线程里,按顺序执行业务,做完一件事再去做下一件事. 异步事件: 在一个线程里,做一个事的同事,可以另起一个新的线程执行另一件事,这样两件事可以同时执行. 用一个例子来解释同步事件和异步事件的使用场景,有时候一段完整的代码逻辑,可能分为几部分,拿最常见的注册来说,假设完整流程是,1.点击注册->2.检验信息并存库->3.发送邮件通知->4.返回给用户.代码这么写是正确,但不是最好的,缺点如下: 逻辑复杂,业务耦合,我们把校验数据并存库和发送邮件写到一个

  • Springboot事件和bean生命周期执行机制实例详解

    目录 @PostConstruct执行机制 ContextRefreshedEvent事件机制 ApplicationStartedEvent事件机制 总结 细节问题 原因 @PostConstruct执行机制 进入SpringApplication#run(java.lang.String…) public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); st

  • python闭包、深浅拷贝、垃圾回收、with语句知识点汇总

    1.1 闭包 1.闭包概念 1. 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包 2. 一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失. 3. 但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束. 2.闭包特点 1. 必须有一个内嵌函数 2. 内嵌函数必须引用外部函数中的变量 3.

  • Yii框架组件的事件机制原理与用法分析

    本文实例讲述了Yii框架组件的事件机制原理与用法.分享给大家供大家参考,具体如下: 在深入分析 Yii 的运行之前,我们先来看一下 Yii 框架中一个很重要的机制 - 事件. Yii 官方参考文档关于组件事件的解释: ======================================================================= 组件事件是一些特殊的属性,它们使用一些称作 事件句柄 ( event handlers )的方法作为其值. 附加 ( 分配 ) 一个方法到

  • vue总线机制(bus)知识点详解

    vue中非父子组件之间通信除了使用vuex,也可以通过bus总线,两者适用场景不同. bus适合小项目.数据被更少组件使用的项目,对于中大型项目 数据在很多组件之间使用的情况 bus就不太适用了.bus其实就是一个发布订阅模式,利用vue的自定义事件机制,在触发的地方通过$emit向外发布一个事件,在需要监听的页面,通过$on监听事件. vuex适用中大型项目.数据在多组件之间公用的情况. 简单介绍两者的区别之后,就要介绍下我在一个项目中遇到的一个场景了,这个场景使用bus可能更加适合些: //

  • 常用的Java数据结构知识点汇总

    目录 1.数据结构分类 2.线性数据结构 2.1数组 2.2可变数组 2.3链表 2.4栈 2.5队列 3.非线性数据结构 3.1树 3.2图 3.3散列表 3.4堆 1. 数据结构分类 按照线性和非线性可以将Java数据结构分为两大类: ①线性数据结构:数组.链表.栈.队列②非线性数据结构:树.堆.散列表.图 2. 线性数据结构 2.1 数组 数组是一种将元素存储于连续内存空间的数据结构,并且要求元素的类型相同. // 定义一个数组长度为5的数组array int[] array = new

  • springboot 事件监听器的案例详解

    目录 前言 引导案例 一.通过实现ApplicationListener接口实现步骤 1.自定义一个事件类(对象),继承ApplicationEvent 2.自定义业务类实现ApplicationListener 接口 3.主线业务发布事件 二.通过添加 @EventListener 注解来实现 三.使用异步 前言 在spring框架中,提供了很多动态灵活且可扩展的机制,开发者可以利用这些机制完成一些巧妙的业务,实现一些业务中的解耦, 引导案例 下面看一个简单的案例, @Configuratio

随机推荐