事件
事件,就是一系列的行为动作。例如:用户在系统页面的操作,在后台会记录下一个个行为日志,可以算做事件;用户支付了一个订单,也算做一个事件。
监听器
监听器,见名识意,指的是监听一个行为动作(事件),如果触发了就执行后续已经准备好的业务逻辑。这与消息队列中消费者监听器类似:监听到通道中有消息传来,就立马去消费。
实践
自定义事件类
//定义数据类型实体类,后面用于将该类型的数据传递到source
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EventInfo {
private String eventName;
private Integer eventType;
}
//定义自定义事件类
@Slf4j
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
log.info("MyEvent is create");
}
}
定义监听类
监听器类有两种方式创建,第一种是实现ApplicationListener接口;另一个是使用@EventListener注解
接口方式
实现ApplicationListener接口,且泛型上界为ApplicationEvent
@Slf4j
@Component
public class MyEventListenerByInterface implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
log.info("eventListener-interface-start");
EventInfo eventInfo = (EventInfo) event.getSource();
log.info(JSONUtil.toJsonStr(eventInfo));
log.info("eventListener-interface-end");
}
}
注解方式
在方法上使用@EventListener注解,并指定事件类型,用于绑定监听器与事件类型
@Slf4j
@Component
public class MyEventListenerByAnnotation {
@EventListener(classes = MyEvent.class)
public void handleEvent(MyEvent event){
log.info("eventListener-annotation-start");
EventInfo eventInfo = (EventInfo) event.getSource();
log.info(JSONUtil.toJsonStr(eventInfo));
log.info("eventListener-annotation-end");
}
}
发布事件
这里我们使用ApplicationContext接口作为发布事件的入口,因为该接口继承自ApplicationEventPublisher
@Slf4j
@RestController
public class MyEventController {
@Resource
private ApplicationContext applicationContext;
@RequestMapping(value = "/event/push",method = RequestMethod.GET)
public BaseResponse eventPush(){
EventInfo eventInfo = new EventInfo("yyh", 25);
log.info("controller-start");
applicationContext.publishEvent(new MyEvent(eventInfo));
log.info("controller-end");
return BaseResponse.responseItem(eventInfo);
}
}
执行结果
这里我们定义了两个监听器类,用于监听同一个MyEvent事件,所以每当调用一次/event/push接口,两个监听器都会触发,结果如下:
可以看到,触发一次事件,两个监听器执行时同步执行,且接口方式的监听器一定最先执行(注解实现时@Order无效),即使都加上了@Order注解。感兴趣的小伙伴可以实践一下。
异步执行
如果想要异步执行,节省接口调用时间,则可以在监听器的处理方法上加@Async注解(注意:启动类上也需要加@EnableAsync,用于允许异步执行),异步执行结果如下:
从截图可以看到,因为两个监听器分别加了@Async,所以单独为它们分配了两个线程执行任务,而且执行两个监听器打印的日志也是交错开来
总结
对于个人理解,ApplicationEvent事件模型配合@Async注解使用时,类似于消息队列的作用。就是不用直接引入消息队列中间件,实现代码解耦。业务场景如下:
1.例如数据同步,在用户在A系统下单时,需要将数据同步到系统B;
2.记录日志可以这么干,但是使用AOP切面更加稳妥方便。
个人疑问:如果不用ApplicationEvent,而只是单独抽出一个类使用@Async也能实现代码解耦,多这一步的目的在哪儿目前还没想明白。