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> {// 事件发生时执行@Overridepublic 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 {@Autowiredprivate ApplicationContext context;@GetMapping("/hello")public void hello() {System.out.println("准备发送事件");context.publishEvent(new MyEvent(this, "welcome to hangge.com"));System.out.println("事件发送完毕");}
}
(4)启动项目测试一下,我们访问 /hello 接口时,控制台输出如下,说明事件机制运行成功。
- 注意:spring 事件是同步的,这意味着发布者线程将阻塞,直到所有监听都完成对事件的处理为止。
2,使用 @EventListener 监听事件
(1)上面样例我们通过实现 ApplicationListener 接口来定义监听器。从 Spring 4.1 开始,可以使用 @EventListener 注解的方法,以自动注册与该方法签名匹配的 ApplicationListener(监听器类同样需要注册为 Spring 的组件)。下面代码的效果同上面是一样的:
// 事件监听器
@Component
public class MyEventListener{// 使用注解实现事件监听@EventListenerpublic 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{@EventListenerpublic AnotherEvent listener1(MyEvent event) {// 处理事件System.out.println("事件监听器 1 接收到事件: " + event.getMessage());return new AnotherEvent(this, "转发" + event.getMessage());}@EventListenerpublic void listener2(AnotherEvent event) {// 处理事件System.out.println("事件监听器 2 接收到事件: " + event.getMessage());}
}
3,使用 @Order 指定优先级
(1)当 Spring 发布一个事件时,会调用所有能处理这个事件的事件监听器方法。如果你有多个事件监听器方法,那么 Spring 会依次调用这些方法。比如下面样例,当发布一个 MyEvent 事件时,Spring 会依次调用这两个方法。
// 事件监听器
@Component
public class MyEventListener{@EventListenerpublic void listener1(MyEvent event) {// 处理事件System.out.println("事件监听器 1 接收到事件: " + event.getMessage());}@EventListenerpublic void listener2(MyEvent event) {// 处理事件System.out.println("事件监听器 2 接收到事件: " + event.getMessage());}
}
(2)我们可以使用 @Order 注解来指定事件监听器方法的优先级。@Order 注解可以标注在类上或方法上,表示这个类或方法的优先级。数值越小,优先级越高。
// 事件监听器
@Component
public class MyEventListener{@Order(2)@EventListenerpublic void listener1(MyEvent event) {// 处理事件System.out.println("事件监听器 1 接收到事件: " + event.getMessage());}@Order(1)@EventListenerpublic void listener2(MyEvent event) {// 处理事件System.out.println("事件监听器 2 接收到事件: " + event.getMessage());}
}
4,使用 @Async 实现异步事件监听
(1)从第一个样例运行结果可以看出默认 spring 事件是同步的,这意味着发布者线程将阻塞,直到所有侦听器都完成对事件的处理为止。我们可以使用 @Async 注解来标注一个事件监听器方法,表示这个方法是一个异步方法,应该在独立的线程中执行。
// 事件监听器
@Component
public class MyEventListener{// 使用注解实现事件监听@Async@EventListenerpublic 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)最后测试一下,可看到实现了异步事件监听:
附:Spring Boot 内置的 Application Event
(1)Spring Boot 中包含了一些与 SpringApplication 生命周期相关的内置 Application Event,包括:
- ApplicationStartingEvent:在 Spring Boot 应用程序启动之前发布。
- ApplicationEnvironmentPreparedEvent:在 Spring Boot 应用程序的环境已经准备好,但正在创建 Application Context 上下文之前发布。
- ApplicationContextInitializedEvent:当 Spring Boot 应用程序 Application Context 上下文准备就绪并且调用
- ApplicationContextInitializers,但尚未加载 bean 定义时发布。
- ApplicationPreparedEvent:在 Spring Boot 应用程序的 Application Context 上下文已经创建,但尚未刷新之前发布。
- ApplicationStartedEvent:在 Spring Boot 应用程序的 Application Context 上下文已经刷新,但尚未启动之前发布。
- ApplicationReadyEvent:在 Spring Boot 应用程序已经启动并准备接受请求之后发布。
- ApplicationFailedEvent:在 Spring Boot 应用程序启动失败时发布。
(2)假设我们需要监听 ApplicationStartingEvent,则首先定义一个监听器类:
// 事件监听器
public class MyEventListener implements ApplicationListener<ApplicationStartingEvent> {// 事件发生时执行@Overridepublic void onApplicationEvent(ApplicationStartingEvent event) {// 处理事件System.out.println("应用程序将要启动");}
}
(3)由于该事件实际上是在 Application Context 创建前触发的,这时的 Bean 是不能被加载的。所以我们不能在这个监听器类上中使用 @Component 注册监听器,只能通过 SpringApplication 注册监听器。我们对项目启动类做如下修改:
注意:对于ApplicationStartedEvent、ApplicationReadyEvent、ApplicationFailedEvent 这些在 Application Context上下文已经创建完毕之后,所以可以直接使用 @Component 注册,不需要下面这个步骤。
@SpringBootApplication
public class RmiserverApplication {public static void main(String[] args) {SpringApplication app = new SpringApplication(RmiserverApplication.class);app.addListeners(new MyEventListener()); //注册监听器app.run(args);}
}
或者也可以使用使用 SpringApplicationBuilder 注册监听器:
@SpringBootApplication
public class RmiserverApplication {public static void main(String[] args) {new SpringApplicationBuilder().sources(RmiserverApplication.class).listeners(new MyEventListener()) //注册监听器.run(args);}
}
(4)启动项目,可以看到控制台输出如下: