今天聊聊sping 的 ApplicationEvent,说实话,这个spring的事件驱动模式用的比较少,以前发现这个的时候也是在网上搜索一下怎么使用旧完事了。今天在代码中又发现了他的身影,就好好扒一扒这个功能。
首先是它的使用方法,在网上一搜一大堆,无非就是以下几个步骤:
1、定义一个事件类继承ApplicationEvent
2、监听该事件类型的消息(使用@EventListener模式)
3、发布事件
完事,点击启动,测试:
好了,使用方式就是上面3部曲,现在有个问题:从最后测试的图来看,左边红框中都是相同的线程名,说明消息消费与消息生产是在同一个线程中,也就是同步的。正常来说我们要是使用这个机制一般都是想使用异步的方式,不想要影响主流程,那怎么才能让消费者进行异步消费?
带着上面的问题,我们看下代码里是怎么执行的。
先在消费者那边打上断点(断点不要钱,使劲打):
从上面的堆栈信息可以看到消息推送到消息消费之间的完整的调用堆栈。
从上图的调用栈对应的代码中可以看出代码里面判断了事件消息是否是ApplicationEvent类型的,我们测试的例子继承了ApplicationEvent,所以走的是if的分支,但是下面还有个else分支,从代码逻辑来看,即使事件类没有继承ApplicationEvent也会给我们包装一下,按道理也是能够正常运行的。
测试一下:
从测试结果来看也是正常的,看来使用三部曲可以更精简点了,第一步继承ApplicationEvent也可以省略了。
再往上追下调用栈:
从上面这块代码看出是可以使用异步方式的,只不过当前executor为空导致的没有使用异步模式,那看看executor是从哪里设置的:
然后看下applicationEventMulticaster是在哪设置的:
从上面可以看出会从spring容器里面找有没有applicationEventMulticaster的bean,没有的话就走else分支new一个SimpleApplicationEventMulticaster出来,那这样的话,我们是不是可以自己配置一个SimpleApplicationEventMulticaster的bean,然后给它配置executor,这样不就实现了异步?
测试一下:
发现确实是使用到了异步线程。当然了,使用异步的方式还可以用@Async的模式,但是使用使用自定义SimpleApplicationEventMulticaster bean的方式逼格稍微高那么一点不是?而且使用这种模式还有一个好处,往下看:
我是不是可以在自己的SimpleApplicationEventMulticaster中设置一个errorHandler?然后异常后按照自己想要的方式进行额外的统一处理?这比在每个监听事件中写异常处理优雅多了吧?
然后我还发现一个有趣的地方:
监听事件是可以有返回值的,而且有返回值后还会再次推送消费。测试一下(增加了返回值):
从上面可以看出确实拿着结果再次推了。那我再建一个监听String类型的消费者看看效果:
看到没,根据返回结果的类型再次消费了。逼格是不是再次提升了一点?这个使用场景也应该有挺多的吧,比如注册功能,注册成功后需要赠送积分,赠送积分后还要发消息通知,那这是不是就可以使用这种方式了,推送积分事件进行异步处理赠送积分,赠送积分的方法再整一个Msg类型的返回值,然后再整一个监听Msg的事件。这样在主流程里面只需要推送一次积分事件就可以了(有点责任链的那味了)。这逼格,你就说高不高?
但是,盆友,这里需要提醒一下,使用返回值的时候需要慎重啊,看个例子:
将上面的那个方法调整了下,之前是void类型的,现在改成String类型的了。
死循环了!!!返回的类型又有对应的监听事件,可不就死循环了嘛。所以在使用返回值的监听事件的时候,需要慎重,多检查一下有没有死循环的风险!!!
总结一下:
1、异步消费方式可以使用@Async模式,也可以自定义名为SimpleApplicationEventMulticaster的bean,并且该bean中可以设置自定义的errorHandler进行统一的异常处理
2、@EventListener方法是可以有返回值的,如果有监听返回值类型的事件,spring还会进行监听该消息进行消费的,但是这个也有死循环的风险,需要小心使用。