zoukankan      html  css  js  c++  java
  • SpringBoot系列——事件发布与监听

      前言

      日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱、短信提示用户,通常我们都是这样写:

        /**
         * 用户注册
         */
        @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、码云:

      GitHub:https://github.com/huanzi-qch/springBoot

      码云:https://gitee.com/huanzi-qch/springBoot

    版权声明

    作者:huanzi-qch
    若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.


    捐献、打赏

    请注意:作者五行缺钱,如果喜欢这篇文章,请随意打赏!

    支付宝

    微信


    QQ群交流群

    QQ群交流群
    有事请加群,有问题进群大家一起交流!

  • 相关阅读:
    asp.net跳转页面的三种方法比较
    DotNet软件开发框架
    asp.net在ie7中使用FileUpload上传前预览图片
    uv贴图详解
    3d之 panda3d
    3dmax9中文版注册机
    flash player本地安全设置
    处理2D图像和纹理——扩展图像内容处理器
    处理2D图像和纹理——扩展图像内容处理器:灰度变换和处理器参数
    处理2D图像和纹理——将场景绘制到纹理
  • 原文地址:https://www.cnblogs.com/huanzi-qch/p/14792984.html
Copyright © 2011-2022 走看看