今天为大家分享一下Spring的事件机制的使用,它是spring中一个非常好用也很实用的机制。
1. spring事件机制的概念
Spring的事件机制是基于观察者模式实现的,它可以在我们的实际应用程序中实现代码之间的解耦,提高代码的可维护性和可扩展性。
Spring的事件机制包括事件、事件发布、事件监听器等几个基本概念。
- 事件是一个抽象的概念,它代表着应用程序中的某个动作或状态的发生。
- 事件发布是事件发生的地方,它负责产生事件并通知事件监听器。
- 事件监听器是事件的接收者,它负责处理事件并执行相应的操作。
在Spring的事件机制中,事件源和事件监听器之间可以通过事件进行通信,来实现模块之间的解耦,提高代码的优雅性和可维护性。
比如:用户修改密码,需要进行短信通知,记录关键性日志或者其他业务操作。
1.1 事件
通过继承ApplicationEvent,实现自定义事件。Spring 中的所有事件都要基于其进行扩展。其源码如下:
1.2 事件发布
事件发布是事件发生地,它负责产生事件并通知事件监听器。ApplicationEventPublisher
用于发布 ApplicationEvent
事件,发布后 ApplicationListener 才能监听到事件进行处理。源码如下:
参数需要一个ApplicationEvent,就是我们的事件,然后通过该方法去发布我们的事件。
1.3 事件监听器
ApplicationListener 是 Spring 事件的监听器,用来接受事件,所有的监听器都必须实现该接口。该接口源码如下:
或者采用注解方式来实现:@EventListener。
2. 案例演示
2.1 定义事件
/**
* @BelongsProject: ims-formworkflow-api
* @BelongsPackage: com.wuk.imsformworkflowapi.client
* @Author: wuk
* @Date: 2023/6/13 9:54
* @Description: 创建事件
*/
@Getter
@Setter
public class UserChangePasswordEvent extends ApplicationEvent {
private String userId;
public UserChangePasswordEvent(String userId) {
super(new Object());
this.userId = userId;
}
}
这里就定义我们监听器需要的业务参数,我们需要监听器监听哪些参数的变化,我们就在这里定义哪些参数。
2.2 监听事件的实现
有两种方式,一种是通过实现接口方式,一种是注解方式,推荐使用注解,耦合性比较低,比较灵活。
方式一:
/**
* @BelongsProject: ims-formworkflow-api
* @BelongsPackage: com.wuk.imsformworkflowapi.client
* @Author: wuk
* @Date: 2023/6/13 9:57
* @Description: 方式1:实现接口方式,实现监听逻辑
*/
@Component
public class MessageListener implements ApplicationListener<UserChangePasswordEvent> {
@Override
public void onApplicationEvent(UserChangePasswordEvent event) {
System.out.println("监听者1:收到事件:" + event);
System.out.println("监听者1:开始执行业务操作给用户发送短信。用户userId为:" + event.getUserId());
}
}
方式二:
/**
* @BelongsProject: ims-formworkflow-api
* @BelongsPackage: com.wuk.imsformworkflowapi.client
* @Author: wuk
* @Date: 2023/6/13 10:24
* @Description: 方式二:注解形式实现事件监听器
*/
@Component
public class ListenerEvent {
@EventListener({ UserChangePasswordEvent.class })
public void LogListener(UserChangePasswordEvent event) {
System.out.println("监听者2:收到事件:" + event);
System.out.println("监听者2:开始执行业务操作生成关键日志。用户userId为:" + event.getUserId());
}
@EventListener({ UserChangePasswordEvent.class })
public void messageListener(UserChangePasswordEvent event) {
System.out.println("监听者3:收到事件:" + event);
System.out.println("监听者3:开始执行业务操作给用户发送短信。用户userId为:" + event.getUserId());
}
}
注意:使用 @EventListener 注解的好处是一个类可以写很多监听器,定向监听不同的事件,或者同一个事件。
这里面还延伸了一个注解:@TransactionalEventListener
,他与@EventListener
不同的就是@EventListener标记一个方法作为监听器,他默认是同步执行。
我们知道,Spring的事件监听机制实际上是同步的。来将代码进行解耦。而@TransactionEventListener
仍是通过这种方式,但是加入了回调的方式来解决,这样就能够在事务进行Commited,Rollback…等时候才去进行Event的处理,来达到事务同步的目的。
2.3 事件发布
@GetMapping("/eventTest")
public R<Void> eventTest(){
applicationEventPublisher.publishEvent(new UserChangePasswordEvent("11111"));
return R.ok();
}
执行完毕后,控制台打印如下:
3. 利用@Async实现事件异步
监听器默认是同步执行的,如果我们想实现异步执行,可以搭配@Async注解使用。
注意:使用@Async时,需要配置线程池,否则用的还是默认的线程池也就是主线程池,线程池使用不当会浪费资源,严重的会出现OOM事故。
我这边只做演示,就不创建线程池了。
3.1 启动类添加@EnableAsync开启异步执行配置
@EnableAsync
@SpringBootApplication
public class SpirngEventApplication {
public static void main(String[] args) {
SpringApplication.run(SpirngEventApplication.class, args);
}
}
3.2 在要异步执行的监听器上添加@Async注解
@Component
public class ListenerEvent {
@Async
@EventListener({ UserChangePasswordEvent.class })
public void logListener(UserChangePasswordEvent event) {
System.out.println("收到事件:" + event);
System.out.println("开始执行业务操作生成关键日志。用户userId为:" + event.getUserId());
}
}
4. 场景应用
- 告警操作,比如钉钉飞书告警,异常告警,可以通过事件机制进行解耦。
- 关键性日志记录和业务埋点。
- 性能监控,比如说接口的时长,性能方便的监控等。可以通过事件机制进行解耦。
总之与主业务无关的操作都可以通过这种方式进行解耦。
5. 注意点
在使用spring事件中我们要注意如下问题:
- 对于同一个事件,有多个监听器的时候,可以通过@Order注解指定顺序,Order的value值越小,执行的优先级就越高。
- 如果发布事件的方法处于事务中,那么事务会在监听器方法执行完毕之后才提交。事件发布之后就由监听器去处理,而不要影响原有的事务,也就是说希望事务及时提交。我们就可以 @TransactionalEventListener来定义一个监听器。
- 监听器默认是同步执行的,如果我们想实现异步执行,可以搭配@Async注解使用。
- 对于同一个事件,有多个监听器的时候,如果出现了异常,后续的监听器就失效了,因为他是把同一个事件的监听器add在一个集合里面循环执行,如果出现异常,需要注意捕获异常处理异常。