SpringBoot系列——事件發佈與監聽
- 2021 年 5 月 21 日
- 筆記
- springboot
前言
日常開發中,我們經常會碰到這樣的業務場景:用戶註冊,註冊成功後需要發送郵箱、短訊提示用戶,通常我們都是這樣寫:
/** * 用戶註冊 */ @GetMapping("/userRegister") public String userRegister(UserVo userVo) { //校驗參數 //存庫 //發送郵件 //發送短訊 //API返回結果 return "操作成功!"; }
可以發現,用戶註冊與信息推送強耦合,用戶註冊其實到存庫成功,就已經算是完成了,後面的信息推送都是額外的操作,甚至信息推送失敗報錯,還會影響API接口的結果,如果在同一事務,報錯信息不捕獲,還會導致事務回滾,存庫失敗。
官方文檔相關介紹://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 "操作成功!"; } }
效果
用戶註冊
//localhost:10010/eventPublish/userRegister?id=1&username=張三
API返回
後台異步任務執行
工單發起
//localhost:10010/eventPublish/workOrderStart?id=1&workOrderName=設備出入申請單
API返回
後台異步任務執行
後記
springboot使用事件發佈與監聽就暫時記錄到這,後續再進行補充。
代碼開源
代碼已經開源、託管到我的GitHub、碼云:
GitHub://github.com/huanzi-qch/springBoot
碼云://gitee.com/huanzi-qch/springBoot