前言
spring的事件驱动是观察者模式的一种实现,其目的是将耦合的代码解耦,方便功能的扩展和维护。
观察者模式简介
一种行为型设计模式,定义了一种一对多的依赖关系,当一个对象的状态发生变化时,通过发布事件的形式通知所有的观察者。观察者模式的核心角色有:
- 主题,也就是被观察者,维护了一个观察者列表,定义了观察者的注册、删除,以及通知方法
- 观察者,接收主题通知的对象,定义了接收到通知时要调用的更新方法
- 具体主题,主题的具体实现,实现了注册、删除和通知方法
- 具体观察者 ,观察者的具体实现,实现了更新方法
Spring中的ApplicationListener对应观察者,ApplicationListener抽象类的实现类对应具体观察者。主题的角色被进一步拆分,由ApplicationEventMulticaster维护观察者列表和提供观察者的注册、删除、通知方法,在业务对象的状态发生变化时,通过ApplicationEventPublisher委托广播器进行事件通知。
Spring还定义了ApplicationEvent事件对象作为数据载体来传递信息。
使用案例
- 定义业务状态对应的ApplicationEvent事件
public class StatusChangeEvent extends ApplicationEvent {
public StatusChangeEvent(DataObject data) {
super(data);
}
}
2.在需要进行状态通知的业务对象里注入ApplicationEventPublisher,在状态变更时发布事件。ApplicationContext容器本身实现了ApplicationEventPublisher接口,在默认情况下注入的就是Spring容器
@Component
public class BusinessObject {
// 不推荐
// @Autowired
// private ApplicationContext applicationContext;
// 推荐,逻辑更清晰
@Autowired
private ApplicationEventPublisher publisher;
public void businessMethod() {
publisher.publishEvent(new StatusChangeEvent(new DataObject()));
}
}
3.定义ApplicationListener,指定监听的业务事件,并实现事件的处理逻辑。可以指定一个类为监听者,也可以指定一个方法。
定义监听器类
@Component
// 需要实现ApplicationListener接口
public class BusinessEventListener implements ApplicationListener<StatusChangeEvent> {
@Override
public void onApplicationEvent(StatusChangeEvent event) {
// 实现事件处理逻辑
}
}
指定一个方法为listener
@Component
public class BusinessObject2 {
@EventListener
public void businessMethod(StatusChangeEvent event) {
DataObject object = (DataObject) event.getSource();
System.out.println(object.getMsg());
}
}
spring提供了默认的ApplicationEventMulticaster——SimpleApplicationEventMulticaster
使用进阶
异步执行
默认情况下监听方法是同步执行的,但不影响主流程的处理逻辑其实可以异步处理,以降低主流程耗时
- 在springboot的启动类上添加@EnableAsync注解
- 监听方法上添加@Async注解并指定线程池
@Async("businessExecutor")
@EventListener
public void asyncMethod(StatusChangeEvent event) {
DataObject object = (DataObject) event.getSource();
System.out.println(Thread.currentThread().getId() + ":" + object.getMsg());
}
条件监听
在监听的事件的属性满足一定条件时才执行事件处理逻辑的场景,可以使用注解的condition属性开启条件监听,方便优雅的实现。
@Component
public class ConditionalListener {
@EventListener(condition = "#event.source.msg=='hello'")
public void conditionalMethod(StatusChangeEvent event) {
DataObject source = (DataObject) event.getSource();
System.out.println("监听成功:msg="+source.getMsg());
}
}
控制监听器执行顺序
有时对一个事件的处理,多个监听器需要按一定顺序执行。此时可以在监听器类或方法上添加@Order注解来控制监听器的执行顺序。设定的值越小越先执行。
@Component
public class OrderListener {
@EventListener
@Order(3)
public void listen3(OrderEvent orderEvent) {
System.out.println("listener 3");
}
@EventListener
@Order(1)
public void listen1(OrderEvent orderEvent) {
System.out.println("listener 1");
}
@EventListener
@Order(2)
public void listen2(OrderEvent orderEvent) {
System.out.println("listener 2");
}
}
应用案例
spring使用事件机制提供扩展点
在spring(abstractApplicationContext#refresh)应用容器刷新完成时(finishRefresh),会广播ContextRefreshedEvent事件,通过实现对应的事件监听器就可插入自定义的扩展逻辑。
sofaboot的健康检查
sofaboot作为蚂蚁内部对springboot的增强框架,优势之一是提供了健康检测机制。当healthcheck-sofa-boot包里定义的ReadinessCheckListener监听到ContextRefreshedEvent事件后,会进行健康检查,当有任何检查失败就会抛异常启动失败。
ReadinessCheckListener的事件处理
public class ReadinessCheckListener implements ApplicationContextAware, PriorityOrdered, ApplicationListener<ContextRefreshedEvent>, InitializingBean {
public void onApplicationEvent(ContextRefreshedEvent event) {
if (this.applicationContext.equals(event.getApplicationContext())) {
// 初始化,从容器里取HealthChecker
this.healthCheckerProcessor.init();
this.healthIndicatorProcessor.init();
this.afterReadinessCheckCallbackProcessor.init();
// 执行所有的HealthChecker的检查方法
this.readinessHealthCheck();
this.readinessCheckFinish = true;
}
}
}
注意事项
同步执行场景下,多个监听器是链式处理的,一个监听器执行失败并抛出异常的话,会导致后续监听器无法监听到事件。此时可以:
- 监听器使用try catch处理异常,不让异常抛出
- 自定义事件广播器并设置一个ErrorHandler用来处理异常,这个广播器的beanName一定要是applicationEventMulticaster
@Bean("applicationEventMulticaster")
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setErrorHandler(t -> {
System.out.println("捕获到了异常:"+t.getMessage());
});
return simpleApplicationEventMulticaster;
}