Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[译]谈谈SpringBoot 事件机制

[译]谈谈SpringBoot 事件机制

作者头像
东溪陈姓少年
发布于 2020-08-06 07:19:07
发布于 2020-08-06 07:19:07
2.7K00
代码可运行
举报
文章被收录于专栏:TECH flowerTECH flower
运行总次数:0
代码可运行

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

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

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


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

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

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

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

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

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

创建ApplicationEvent

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class UserCreatedEvent extends ApplicationEvent {
  private String name;

  UserCreatedEvent(Object source, String name) {
    super(source);
    this.name = name;
  }
  ...
}

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class UserRemovedEvent {
  private String name;

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

发布一个ApplicationEvent

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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仅在某些情况下才允许触发我们的侦听器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {

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

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

异步事件侦听器

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
class AsyncListener {

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

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

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

Transaction-绑定事件

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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。我们可以通过手动添加侦听器来注册这些事件的侦听器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TECH flower 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【MySql】表的约束
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是email,要求是唯一的。表中一定要有各种约束,通过约束,让我们未来插入数据库表中的数据是符合预期的。约束的本质是通过技术收到逼迫程序员插入正确的数据,反过来,站在mysql的视角,凡是插入进来的数据,都是符合数据约束的。约束的最终目标:保证数据的完整性和可预期性所以需要更多的约束。 表的约束很多,这里主要介绍如下几个: null/not null,default, comment, zerofill,primarykey,auto_increment,unique key 。
平凡的人1
2023/10/15
5180
【MySql】表的约束
初谈表的约束
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是email,要求是唯一的。 表的约束很多,这里主要介绍如下几个: null/not null,default, comment, zerofill,primarykey,auto_increment,unique key
南桥
2024/08/05
1200
初谈表的约束
MySQL数据库:表的约束
表的约束,实质上就是用数据类型去约束字段,但是数据类型的约束手法很单一,比如,我们在设置身份证号这个字段,数据类型唯一起的约束是它属于char类型或者varchar类型,不能是浮点型也不能是日期时间类型,但是这样还不够,身份证号需要有唯一性,是每个合法公民的唯一标识。因此需要额外增加一些手段去进行约束,以便更好的保证数据的合法性。
二肥是只大懒蓝猫
2023/10/13
8400
MySQL数据库:表的约束
表的约束(MySQL)
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是email,要求是唯一的。
ljw695
2025/05/31
740
表的约束(MySQL)
MySQL数据库表约束详解
表中一定要有各种约束,通过约束,让我们未来插入数据库表中的数据是符合预期的。约束本质是通过技术手段,倒逼程序员,插入正确的数据。
用户11316056
2025/02/22
4550
MySQL数据库表约束详解
MySQL:表的约束
表的约束:表中一定要有各种约束,通过约束,让我们未来插入数据库中的数据是符合预期的。约束本质是通过技术手段,倒逼程序员,插入正确的数据。反过来,站在MySQL角度,凡是插入进来的数据,都是符合数据约束的!
小灵蛇
2024/11/22
3400
MySQL:表的约束
mysql-完整性约束
mysql> create table tb1(id int not null default 2,num int not null);  # 创建tb1表并约束
py3study
2018/08/03
8710
深入浅出:掌握MySQL表约束与数据完整性设计
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是email,要求是唯一的。表的约束很多,这里主要介绍如下几个: null/not null,default, comment, zerofill,primary key,auto_increment,unique key 。
用户11286421
2025/03/20
1390
深入浅出:掌握MySQL表约束与数据完整性设计
【Mysql】:linux环境下表的三部曲(数据操作 + 类型解析 + 约束规则)
这篇文章只是对以前的内容作个详细补充,想要速成操作的,可以参考这篇博客 【MySQL学习】:关系数据库标准语言SQL
IsLand1314
2025/02/09
1710
【Mysql】:linux环境下表的三部曲(数据操作 + 类型解析 + 约束规则)
MySQL表的完整性约束
  为了防止不符合规范的数据进入数据库,在用户对数据进行插入、修改、删除等操作时,DBMS自动按照一定的约束条件对数据进行监测,使不符合规范的数据不能进入数据库,以确保数据库中存储的数据正确、有效、相容。
changxin7
2019/09/10
3.6K0
MySQL之表的约束
一 介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性 主要分为: PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 FOREIGN KEY (FK) 标识该字段为该表的外键 NOT NULL 标识该字段不能为空 UNIQUE KEY (UK) 标识该字段的值是唯一的 AUTO_INCREMENT 标识该字段的值自动增长(整数类型,而且为主键) DEFAULT 为该字段设置默认值 UNSIGNED 无符号 Z
人生不如戏
2018/04/10
4.9K0
【MySQL】数据库约束
喜欢做梦
2025/04/20
1990
【MySQL】数据库约束
数据库约束-主键约束-唯一约束-非空约束-默认值
约束其实就是一种限制,用于修饰表中的列. 通过这种限制来保证表中数据的正确性、有效性和完整性。
Devops海洋的渔夫
2022/01/17
6.7K0
数据库约束-主键约束-唯一约束-非空约束-默认值
第13章_约束
数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
程序员Leo
2023/08/07
6310
第13章_约束
MySQL数据库,从入门到精通:第十三篇——MySQL数据表约束详解
在MySQL数据库中,约束是一种对数据表中数据进行限制和检查的方法,可以保证数据表中数据的完整性和一致性。本文将深入剖析MySQL中的各种约束,包括非空约束、唯一性约束、主键约束、自增列、外键约束、默认值约束以及CHECK约束等等,同时结合开发场景给出约束使用和实践的技巧和方法,帮助读者更好地掌握MySQL中数据表相关操作的技巧和方法。
默 语
2024/11/20
5040
MySQL数据库,从入门到精通:第十三篇——MySQL数据表约束详解
MySQL数据库2表的增删改查
每个字段由若干按照某种界限划分的相同数据类型的数据项组成,这里指的数据表中的列,一列就是一个字段
GH
2019/12/12
13.5K0
2024Mysql And Redis基础与进阶操作系列(4)作者——LJS[含MySQL FOREIGN KEY、CHECK 、DEFAULT 约束举例说明等详解步骤及常见报错问题所对应的解决方法]
例如:学生表、课程表、选课表:选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是 主表,选课表是从表。
盛透侧视攻城狮
2024/10/22
2260
2024Mysql And Redis基础与进阶操作系列(4)作者——LJS[含MySQL FOREIGN KEY、CHECK 、DEFAULT 约束举例说明等详解步骤及常见报错问题所对应的解决方法]
表——完整约束性规则(键)
一 介绍 约束条件与数据类型的宽度一样,都是可选参数 作用:用于保证数据的完整性和一致性 主要分为: primary key (PK) 标识该字段为该表的主键,可以唯一的标识记录 foreign key (FK) 标识该字段为该表的外键 not null 标识该字段不能为空 unique key (UK) 标识该字段的值是唯一的 auto_increment 标识该字段的值自动增长(整数类型,而且为主键) default 为该字段设置默认值 unsigned 无符号 z
用户1214487
2018/01/23
2.5K0
MySQL(数据类型和完整约束)
default默认值,创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值
全栈程序员站长
2022/07/21
1K0
再谈表的约束
auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。 自增长的特点:
南桥
2024/08/15
1250
再谈表的约束
相关推荐
【MySql】表的约束
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验