网络上比较推崇使用Spring Event 优雅的实现观察者模式。我在调研后也确实觉得这种方式能实现业务逻辑上的解耦,但线上的一次事故,让我意识到 Spring Event远远没有那么简单。
前几天,线上系统出现两条异常日志Get Bean时找不到对应的bean,调用堆栈让我非常迷惑,为什么Get Bean找不到对应的Bean呢? 堆栈中的信息 解释了原因。
Do not request a bean from a BeanFactory in a destroy method implementation
在应用上下文关闭时,不得从上下文中Get Bean。 恰好,问题出现在服务关闭期间..... 由于系统流量较高,日订单千万级,即便在低峰期单机的并发度也是比较高的,所以服务在关闭期间有少量流量进来或未处理完。
要明白问题原因和解决办法,需要先简单了解 Spring Event如何使用,总共分为三步。
自定义事件需要继承Spring ApplicationEvent。我选择使用泛型,支持子类可以灵活关联事件的内容。
public class BaseEvent<T> extends ApplicationEvent {
private final T data;
public BaseEvent(T source) {
super(source);
this.data = source;
}
public T getData() {
return data;
}
}
使用Spring上下文 ApplicationContext发布事件
发布事件复制代码
applicationContext.publishEvent(new BaseEvent<>(param));
Idea为Spring提供了跳转工具,点击绿色按钮位置,就可以 跳转到事件的监听器列表。
监听器只需要 在方法上声明为 EventListener注解,Spring就会自动找到对应的监听器。Spring会根据方法入参的事件类型和 发布的事件类型 自动匹配。
@EventListenerpublic void handleEvent(BaseEvent<PerformParam> event) {
//消费事件
}