前言

最近在项目中做了一项优化,对业务代码进行解耦。我们部门做的是警用系统,通俗的说,可理解为110报警。一条警情,会先后经过接警员、处警调度员、一线警员,警情是需要记录每一步的日志,是要可追溯的,比如报警人张小三在2019-12-02 00:02:01时间报警,接警员A在1分钟后,将该警情记录完成,并分派给处警调度员B,调度员B在5分钟后,分派给一线警员C,C赶到现场后,花了1个小时处理完成。

这中间,每一个接口,需要做的事情,可能就包括了:警情日志记录;警员当前任务数统计,包括待处置的任务和已经处置完成的任务;我们其实还有一个操作,就是发mq,去通知其他相关人,比如接警员A接警完成后,要发mq通知其主管。

以前的代码可能是这样的:

## 接口1里, 接收警情service里完成以下操作 void 接收警情(xxxReqVo reqVo){   1:写库   2:记录警情跟踪日志   3:增加当前接警员的接警数   4:发mq通知其他相关人 }  ##接口2里,分派警情的service里完成以下操作 void 分派警情(xxxReqVo reqVo){   1:写库   2:记录警情跟踪日志   3:增加当前处警调度警员的处警数   4:发mq通知其他相关人 }

这样的问题是什么呢?

  1. 在每一个相关接口里,都要“显式”调用:记录跟踪日志的相关方法、统计相关的方法、发mq相关的方法;但凡有一个地方忘记了,都会导致问题,比如统计数量不准,mq忘发,跟踪日志遗漏等。
  2. 业务逻辑和这类通用业务揉在一起,假设下次又需要给报警人发个短信,岂不是又得去改核心代码?这不符合我们“对修改关闭,对扩展开放”的开闭原则啊;假设脑残的产品经理,这次说要给报警人发短信,过两天又不要了,难道每个接口,挨个挨个改吗,想想都想打死产品经理,但是这个又犯法,还是想想其他办法?

这个问题,我们可以用类似mq的方法来解决,即,发送消息,各个消费者去消费。一般,mq的方式适用于微服务之间,而我们这里,将使用事件-发布机制来解决这个问题。
源码地址(直接dubug跟一下,很简单,比看文章来得快):
https://gitee.com/ckl111/spring-event-publish-demo

先说说ApplicationListener

spring boot之前的spring 时代,想必一些同学用过org.springframework.context.ApplicationListener,正好我手里有一个老项目,也用到了这个东西,我就拿这个举个例子:

在我们的项目中,需要在启动后,初始化一些东西,比如预热缓存,最早的代码呢,可能是大家各自实现org.springframework.beans.factory.InitializingBean,但是这样呢,初始化代码散落在各个service中;还有一些直接使用@PostContruct注解,然后在对应方法里去完成一些初始化操作。但是总体来说,这些方式,在spring的启动过程中,被调用的时机比较靠前,有些候某些bean可能还没初始化完成,而导致一些奇怪的问题。

所以,我们后来统一去掉了这些初始化代码,全部采用以下机制来实现:

import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent;  @Service public class InitRunner implements ApplicationListener<ContextRefreshedEvent> {      @Autowired     private InitService initService;      @Override     public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {         //root application context,因为是web项目,         if (contextRefreshedEvent.getApplicationContext().getParent() == null) {               initService.init();         }  }

在这个类中,我们实现了org.springframework.context.ApplicationListener<ContextRefreshedEvent>,这个 listener的定义如下:

public interface ApplicationListener<E extends ApplicationEvent>