Springboot 异步事件配置和使用
Spring 中提供了完整的事件处理机制,本身底层内置实现了一些事件和监听,同时支持开发者扩展自己的事件和监听实现。
一般这种基于事件的实现在项目实际开发中我们主要用来解耦,和做异步处理(默认是同步),提供应用的响应速度。
核心架构
先简要看一下,在 Spring 中要实现自定义事件监听需要涉及哪些接口类,这里忽略异步的引用、注解的实现,后面会说到。
基本实现步骤
自定义事件:一般继承自 ApplicationEvent 即可,注意里面要去定义和实现自己的事件方法,也就是具体这个事件要做什么事,一般就在事件类、或者基于事件类去实现即可。
事件发布:业务代码中注入 ApplicationEventPublisher 类,然后再具体业务方法中调用 publishEvent 方法,传入上面自定义的事件,以及自定义的必要参数等信息
实现事件监听:有了事件、也发布了,那必须有对应的监听来调用具体的事件,一般实现 ApplicationListener 泛型传入自己的事件类型即可
注意事项
异常和事物:默认情况下事件的发布、监听处理都是和当前业务线程绑定到一起的,也就是在同一个线程中操作事件任务。因此无论是事件发布时导致异常,或者是具体事件任务实现的方法异常,都会导致当前业务异常;相应的如果当前业务有事物,那么异常了也会回滚。
事件类型:首先一定要自定义自己的事件,其次在监听的时候也是监听自己的事件,而不是监听基类或者接口然后去判断,这样反而失去了基于事件监听编程灵活性,同时也违法开闭原则,并不利于后期扩展。具体事件中可以定义其他一些额外的参数,这样方便在具体方法中传参使用
事件顺序:一次可以发布多个事件,无论是同一个还是不同的,执行顺序默认也是按照发布顺序。
场景应用
这里以订单完成和推送给平台订单相关数据为业务模型来举例说明。Spring4.2 之后提供了注解来实现事件监听,非常的方便,这里我们使用注解的方式实现监听即可。
缩略的业务类:包含事件的发布
具体事件的定义:继承自 ApplicationEvent
监听实现:使用注解,注意这里我使用了 事务监听注解 ,按照具体业务场景可以选择具体的注解,比如最常用的 @EventListener。因为我这里的诉求是当前事物提交完成之后再去推送消息,而且实际情况是启用了异步监听来实现,同时有的人在监听的方法中可能还执行了回查,也就是去查询业务中提交的数据,那如果这里不标记为事物提交之后执行,在异步情况下无法获取到数据
异步实现
所谓异步实现,一般是指异步监听,将主体业务逻辑和消息监听任务放到不同的线程去执行,提高业务的响应速度。
Springboot 中我们有多个办法来实现异步监听执行,最简单、最直接的就和异步方法实现一模一样,只需在监听方法上加上 @Async 注解(前提是启用了异步执行)
第一种办法:Configuration 配置类中加上注解 @EnableAsync,启用 Spring 的异步方法执行能力。然后在监听方法上加上 @Async 注解,标明此方法是异步执行。Over 就这样就行了【我们没有配置异步线程对不对?那是会直接 new Thread()来执行异步任务吗,当然不是,而是 Spring 默认提供并初始化了一个专门用来执行异步任务的线程池 ThreadPoolTaskExecutor,会接管所有的异步任务在同一个线程池中执行。也支持定制化处理,后续我们会说到】
第二种办法:如果说不想全局开启异步,只是想给事件监听的代码实现异步任务呢?那最简单就是直接在监听哪里 new Thread().start(),不受控、不优雅,但是业务场景简单,访问量小的情况下也不是不可以。那要规范一点呢,就是自己创建一个线程池,比如 ExecutorService executorService = Executors.newCachedThreadPool();然后在 event.send 哪里使用 executorService.execute(..)执行即可。
第三种办法:优雅点实现,创建 SimpleApplicationEventMulticaster 的 Bean,然后创建一个线程池给塞进去,注意需要把自定义实现注入到 Spring 容器中。其他代码不用做任何修改,就像同步逻辑一样,在事件发布的时候广播会使用 multicastEvent 调用 taskExecutor 获取一个线程去执行监听任务
框架原理
为什么异步监听只需要 @EnableAsync、以及在方法上加上 @Async 就可以了呢?
当我们使用 Springboot,引入 starter 时会自动引入 spring-boot-autoconfigure,此包里面实现了很多自动配置的功能(约定大于配置)名字都是 xxxAutoConfiguration,比如我们这里要说的就是 TaskExecutionAutoConfiguration,容器启动的时候就会加载和创建默认的任务线程池,可以通过 spring.task.execution 开头属性来配置。需要注意的是,无论是否加入 @EnableAsync 注解 TaskExecutionAutoConfiguration 都会初始化一个默认的线程池,因为这个是全局的。
@EnableAsync 的作用是在容器启动的时候,告诉 Spring 我可要支持异步处理任务了,你看着办。Spring 所好的朋友,我给你准备了一个专门搞事的拦截器。
当我们加入了注解,Spring 会将按照配置将准备工作全部做完,从而做到开箱即用,直接一步到位。
总结
Spring 事件模型的四个核心:事件源也就是业务方、事件、广播器、监听器
事件机制支持同步、异步,按需调整和使用。使用异步监听时,推荐使用线程池管理线程,高效、稳定而且易于维护。
使用 Springboot 时通过注解的方式监听、启用异步尽享丝滑。实际原理核心就是观察者模式。
文章转载自:冰雪女娲
评论