SpringBoot - 事件机制使用详解(ApplicationEvent、ApplicationListener)
Spring 事件机制使用观察者模式来传递事件和消息。我们可以使用 ApplicationEvent
类来发布事件,然后使用 ApplicationListener
接口来监听事件。当事件发生时,所有注册的 ApplicationListener
都会得到通知。事件用于在松散耦合的组件之间交换信息。由于发布者和订阅者之间没有直接耦合,因此可以在不影响发布者的情况下修改订阅者,反之亦然。下面通过样例样式事件机制的使用。
1,基本用法
(1)首先我们创建一个自定义事件类 MyEvent
,该类继承自 ApplicationEvent
类。
// 自定义事件类
public class MyEvent extends ApplicationEvent {
private String message;
public MyEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
(2)接着定义一个事件监听器类 MyEventListener
,该类实现 ApplicationListener
接口,并注册为 Spring 的组件。只要监听器对象在 Spring 应用程序上下文中注册,它就会接收事件。当 Spring 路由一个事件时,它使用监听器的签名来确定它是否与事件匹配。
// 事件监听器
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
// 事件发生时执行
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("接收到事件: " + event.getMessage());
// 模拟事件处理
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
(3)最后我们可以使用 ApplicationContext
的 publishEvent
方法来发布事件。
@RestController
public class HelloController {
@Autowired
private ApplicationContext context;
@GetMapping("/hello")
public void hello() {
System.out.println("准备发送事件");
context.publishEvent(new MyEvent(this, "welcome to tubring.cn"));
System.out.println("事件发送完毕");
}
}
(4)启动项目测试一下,我们访问 /hello
接口时,控制台输出如下,说明事件机制运行成功。
准备发送事件
接收到事件: welcome to ...
事件发送完毕
注意:spring 事件是同步的,这意味着发布者线程将阻塞,直到所有监听都完成对事件的处理为止。
2,使用 @EventListener 监听事件
(1)上面样例我们通过实现 ApplicationListener
接口来定义监听器。从 Spring 4.1 开始,可以使用 @EventListener
注解的方法,以自动注册与该方法签名匹配的 ApplicationListener
(监听器类同样需要注册为 Spring 的组件)。下面代码的效果同上面是一样的:
// 事件监听器
@Component
public class MyEventListener{
// 使用注解实现事件监听
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println("接收到事件: " + event.getMessage());
// 模拟事件处理
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
(2)我们可以使用 @EventListener
注解的 value
属性来指定我们要监听的事件类型。比如下面代码来监听 MyEvent
类型的事件:
如果需要同时指定多个事件类型可以这么写:@EventListener({MyEvent.class,AnotherEvent.class})
。
// 事件监听器
@Component
public class MyEventListener{
// 使用注解实现事件监听
@EventListener(MyEvent.class)
public void onApplicationEvent(MyEvent event) {
System.out.println("接收到事件: " + event.getMessage());
}
}
(3)我们可以使用 @EventListener
注解的 condition
属性来指定事件监听器的执行条件。在下面的代码中,#event.message == ‘hello’
是一个 SpEL 表达式,表示当事件的 message
属性值为 hello
时,事件监听器才会被执行。
(1)SpEL (Spring Expression Language) 是一种强大的表达式语言,用于在运行时执行各种表达式。我们可以使用 SpEL 表达式来访问对象的属性、调用对象的方法、执行运算等。
(2)SpEL 表达式语法如下:
- 属性访问:使用 . 操作符访问对象的属性,例如,
object.property
表示访问对象object
的属性property
。 - 方法调用:使用 () 操作符调用对象的方法,例如,
object.method()
表示调用对象object
的方法method
。 - 运算符:SpEL 支持常用的运算符,包括算术运算符、关系运算符、逻辑运算符等。
(3)SpEL 表达式还支持一些特殊的操作符和函数,如下所示:
- ? 操作符:三目运算符,形如 (condition ? then : else),表示当 condition 为真时返回 then,否则返回 else。
instanceof
操作符:用于判断对象是否为某个类型,形如object instanceof T
,表示对象object
是否为类型T
。t()
函数:将对象转换为给定的类型,形如t(T)
,表示将对象转换为类型T
。elvis
操作符:用于判断对象是否为空,形如object ?: defaultValue
,表示如果对象不为空则返回对象,否则返回默认值。
// 事件监听器
@Component
public class MyEventListener{
// 使用注解实现事件监听
@EventListener(condition = "#event.message == 'hello'")
public void onApplicationEvent(MyEvent event) {
System.out.println("接收到事件: " + event.getMessage());
}
}
(4)对于使用 @EventListener
注解并定义为具有返回类型的方法,Spring 会将结果作为新事件发布。在下面的示例中,第一个方法返回的 AnotherEvent
将被发布,然后由第二个方法处理。
// 事件监听器
@Component
public class MyEventListener{
@EventListener
public AnotherEvent listener1(MyEvent event) {
// 处理事件
System.out.println("事件监听器 1 接收到事件: " + event.getMessage());
return new AnotherEvent(this, "转发" + event.getMessage());
}
@EventListener
public void listener2(AnotherEvent event) {
// 处理事件
System.out.println("事件监听器 2 接收到事件: " + event.getMessage());
}
}
3,使用 @Order 指定优先级
(1)当 Spring 发布一个事件时,会调用所有能处理这个事件的事件监听器方法。如果你有多个事件监听器方法,那么 Spring 会依次调用这些方法。比如下面样例,当发布一个 MyEvent
事件时,Spring 会依次调用这两个方法。
// 事件监听器
@Component
public class MyEventListener{
@EventListener
public void listener1(MyEvent event) {
// 处理事件
System.out.println("事件监听器 1 接收到事件: " + event.getMessage());
}
@EventListener
public void listener2(MyEvent event) {
// 处理事件
System.out.println("事件监听器 2 接收到事件: " + event.getMessage());
}
}
准备发送事件
事件监听器 1 接收到事件: ...
事件监听器 2 接收到事件: ...
事件发送完毕
(2)我们可以使用 @Order
注解来指定事件监听器方法的优先级。@Order
注解可以标注在类上或方法上,表示这个类或方法的优先级。数值越小,优先级越高。
// 事件监听器
@Component
public class MyEventListener{
@Order(2)
@EventListener
public void listener1(MyEvent event) {
// 处理事件
System.out.println("事件监听器 1 接收到事件: " + event.getMessage());
}
@Order(1)
@EventListener
public void listener2(MyEvent event) {
// 处理事件
System.out.println("事件监听器 2 接收到事件: " + event.getMessage());
}
}
准备发送事件
事件监听器 2 接收到事件: ...
事件监听器 1 接收到事件: ...
事件发送完毕
4,使用 @Async 实现异步事件监听
(1)从第一个样例运行结果可以看出默认 spring 事件是同步的,这意味着发布者线程将阻塞,直到所有监听器都完成对事件的处理为止。我们可以使用 @Async
注解来标注一个事件监听器方法,表示这个方法是一个异步方法,应该在独立的线程中执行。
// 事件监听器
@Component
public class MyEventListener{
// 使用注解实现事件监听
@Async
@EventListener
public void onApplicationEvent(MyEvent event) {
System.out.println("接收到事件: " + event.getMessage());
// 模拟事件处理
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
(2)同时我们还需要在配置类(@Configuration
类之一或 @SpringBootApplication
类)中启用异步处理,才能使用 @Async
注解。
@Configuration
@EnableAsync
public class MyConfig {
// 配置类
}
(3)最后测试一下,可看到实现了异步事件监听:
准备发送事件
事件发送完毕
接收到事件:welcome to ...