前言
日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱、短信提示用户,通常我们都是这样写:
/** * 用户注册 */ @GetMapping("/userRegister") public String userRegister(UserVo userVo) { //校验参数 //存库 //发送邮件 //发送短信 //API返回结果 return "操作成功!"; }
可以发现,用户注册与信息推送强耦合,用户注册其实到存库成功,就已经算是完成了,后面的信息推送都是额外的操作,甚至信息推送失败报错,还会影响API接口的结果,如果在同一事务,报错信息不捕获,还会导致事务回滚,存库失败。
官方文档相关介绍:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-application-events-and-listeners
本文记录springboot使用@EventListener监听事件、ApplicationEventPublisher.publishEvent发布事件实现业务解耦。
代码
项目结构
默认情况下,事件的发布和监听操作是同步执行的,我们先配置一下async,优雅多线程异步任务,详情请戳:SpringBoot系列——@Async优雅的异步调用
启动类添加@EnableAsync注解
/** * 异步任务线程池的配置 */ @Configuration public class AsyncConfig { private static final int MAX_POOL_SIZE = 50; private static final int CORE_POOL_SIZE = 20; @Bean("asyncTaskExecutor") public AsyncTaskExecutor asyncTaskExecutor() { ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor(); asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE); asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE); asyncTaskExecutor.setThreadNamePrefix("async-task-"); asyncTaskExecutor.initialize(); return asyncTaskExecutor; } }
多数情况下的业务操作都会涉及数据库事务,可以使用@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)注解开启事务监听,确保数据入库后再进行异步任务操作。
定义事件源
先定义两个事件源,继承ApplicationEvent
/** * 用户Vo */ @Data public class UserVo { private Integer id; private String username; } /** * 用户事件源 */ @Getter @Setter public class UserEventSource extends ApplicationEvent { private UserVo userVo; UserEventSource(UserVo userVo) { super(userVo); this.userVo = userVo; } }
/** * 业务工单Vo */ @Data public class WorkOrderVo { private Integer id; private String WorkOrderName; } /** * 业务工单事件源 */ @Getter @Setter public class WorkOrderEventSource extends ApplicationEvent { private cn.huanzi.qch.springbooteventsandlisteners.pojo.WorkOrderVo WorkOrderVo; WorkOrderEventSource(WorkOrderVo WorkOrderVo) { super(WorkOrderVo); this.WorkOrderVo = WorkOrderVo; } }
监听事件
监听用户注册事件、监听业务工单发起事件
/** * 事件监听 */ @Slf4j @Component public class EventListenerList { /** * 用户注册事件监听 */ @Async("asyncTaskExecutor") @EventListener @Order(1)//一个事件多个事监听,在同步的情况下,使用@order值越小,执行顺序优先 public void userRegisterListener(UserEventSource eventSourceEvent){ log.info("用户注册事件监听1:"+eventSourceEvent.getUserVo()); //开展其他业务,例如发送邮件、短信等 } /** * 用户注册事件监听 */ @Async("asyncTaskExecutor") @EventListener @Order(2)//一个事件多个事监听,在同步的情况下,使用@order值越小,执行顺序优先 public void userRegisterListener2(UserEventSource eventSourceEvent){ log.info("用户注册事件监听2:"+eventSourceEvent.getUserVo()); //开展其他业务,例如发送邮件、短信等 } /** * 业务工单发起事件监听 */ @Async("asyncTaskExecutor") @EventListener public void workOrderStartListener(WorkOrderEventSource eventSourceEvent){ log.info("业务工单发起事件:"+eventSourceEvent.getWorkOrderVo()); //开展其他业务,例如发送邮件、短信等 } }
发布事件
创建一个controller,新增两个测试接口
/** * 事件发布 */ @Slf4j @RestController @RequestMapping("/eventPublish/") public class EventPublish { @Autowired private ApplicationEventPublisher applicationEventPublisher; /** * 用户注册 */ @GetMapping("userRegister") public String userRegister(UserVo userVo) { log.info("用户注册!"); //发布 用户注册事件 applicationEventPublisher.publishEvent(new UserEventSource(userVo)); return "操作成功!"; } /** * 业务工单发起 */ @GetMapping("workOrderStart") public String workOrderStart(WorkOrderVo workOrderVo) { log.info("业务工单发起!"); //发布 业务工单发起事件 applicationEventPublisher.publishEvent(new WorkOrderEventSource(workOrderVo)); return "操作成功!"; } }
效果
用户注册
http://localhost:10010/eventPublish/userRegister?id=1&username=张三
API返回
后台异步任务执行
工单发起
http://localhost:10010/eventPublish/workOrderStart?id=1&workOrderName=设备出入申请单
API返回
后台异步任务执行
后记
springboot使用事件发布与监听就暂时记录到这,后续再进行补充。
代码开源
代码已经开源、托管到我的GitHub、码云: