ApplicationEventPublisher
ApplicationEventPublisher是一个事件发布器,我们可以通过ApplicationContext来发布一个相应的事件
主要涉及到 事件定义、事件发布、事件订阅 三个模块
demo
事件
需要继承org.springframework.context.ApplicationEvent
/*** 定义我的事件*/
public class MyEvent extends ApplicationEvent {private String msg;public MyEvent(String msg) {super(msg);this.msg = msg;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}
发布
使用applicationEventPublisher发布事件即可
/*** 发布方** @author * @since 2024/12/4 20:30*/
@Component
public class MyPublisher implements ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher;private int id = 0;/*** 起了个定时任务,用来测试事件发布*/@Scheduled(cron = "*/30 * * * * ?")public void loop() {publish();}public void publish() {System.out.println("publish event. id: " + id);applicationEventPublisher.publishEvent(new MyEvent("new event " + id + " at " + System.currentTimeMillis()));id++;}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}
}
订阅
实现org.springframework.context.ApplicationListener,注入到spring容器中。在泛型中指定需要监听的事件类型
/*** 订阅方*/
@Component
public class MyListener implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("receive a event. msg : " + event.getMsg());}
}
测试结果:
publish event. id: 0
receive a event. msg : new event 0 at 1733368770009
publish event. id: 1
receive a event. msg : new event 1 at 1733368800008
publish event. id: 2
receive a event. msg : new event 2 at 1733368830011
publish event. id: 3
receive a event. msg : new event 3 at 1733368860007
其他事件监听方式
使用注解@EventListener
@Component
public class MyListener2 {@EventListenerpublic void onApplicationEvent(MyEvent event) {System.out.println("receive a event. msg : " + event.getMsg());}
}
其他
- 整个发布流程是同步的还是异步的?
默认同步的。发布方和订阅方共用一个线程。
// 发布public void publish() {id++;System.out.printf("publish event start. id: %s. threadName: %s %n", id, Thread.currentThread().getName());applicationEventPublisher.publishEvent(new MyEvent("new event " + id + " at " + System.currentTimeMillis()));System.out.printf("publish event start. id: %s. threadName: %s %n", id, Thread.currentThread().getName());}// 订阅@EventListenerpublic void onApplicationEvent(MyEvent event) throws InterruptedException {sleep(5000);System.out.printf("receive a event. msg : %s. threadName: %s %n", event.getMsg(), Thread.currentThread().getName());}// 测试结果
// publish event start. id: 1. threadName: scheduled-task-thread2
// receive a event. msg : new event 1 at 1733369970012. threadName: scheduled-task-thread2
// publish event start. id: 1. threadName: scheduled-task-thread2
- 如何异步消费事件
给订阅方上增加@Async注解,这样需要每次增加订阅方都增加注解 - 如何保证消费顺序
当一个事件有多个消费者时,如果需要保证消费次序,可以使用注解@Order - MQ和spring event的区别
MQ更适合于应用级别的解耦,而spring event适用于应用内的解耦,更轻量
相对MQ而言,spring event在系统故障时,由于数据是存在内存,会发生数据丢失 - 适用场景
- 业务逻辑解耦。例如用户下单成功后,会触发库存减少、订单创建、支付请求等动作
- 异步执行。适用于一些发布端不需要关心消费端如何处理,或异步线程不需要堵塞主线程的场景。如日志打印