zoukankan      html  css  js  c++  java
  • 在SpringBoot中实现异步事件驱动

    在项目实际开发过程中,我们有很多这样的业务场景:一个事务中处理完一个业务逻辑后需要跟着处理另外一个业务逻辑,伪码大致如下:

    @Service
    public class ProductServiceImpl {
        ...
        public void saveProduct(Product product) {
            productMapper.saveOrder(product);
            notifyService.notify(product);
        }
        ...
    }

    很简单并且很常见的一段业务逻辑:首先将产品先保存数据库,然后发送通知。

    某一天你们可能需要把新增的产品存到Es中,这时候也需要代码可能变成这样:

    @Service
    public class ProductServiceImpl {
        ...
        public void saveProduct(Product product) {
            productMapper.saveProduct(product);
            esService.saveProduct(product)
            notifyService.notify(product);
        }
        ...
    }

    随着业务需求的变化,代码也需要跟着一遍遍的修改。而且还会存在另外一个问题,如果通知系统挂了,那就不能再新增产品了。

    对于上面这种情况非常适合引入消息中间件(消息队列)来对业务进行解耦,但并非所有的业务系统都会引入消息中间件(引入会第三方架构组件会带来很大的运维成本)。

    Spring提供了事件驱动机制可以帮助我们实现这一需求。

    Spring事件驱动
    spring事件驱动由3个部分组成:

    1. ApplicationEvent:表示事件本身,自定义事件需要继承该类,用来定义事件
    2. ApplicationEventPublisher:事件发送器,主要用来发布事件
    3. ApplicationListener:事件监听器接口,监听类实现ApplicationListener 里onApplicationEvent方法即可,也可以在方法上增加@EventListener以实现事件监听。

    实现Spring事件驱动一般只需要三步:

    1. 自定义需要发布的事件类,需要继承ApplicationEvent类
    2. 使用ApplicationEventPublisher来发布自定义事件
    3. 使用@EventListener来监听事件

    这里需要特别注意一点,默认情况下事件是同步的。即事件被publish后会等待Listener的处理。如果发布事件处的业务存在事务,监听器处理也会在相同的事务中。如果需要异步处理事件,可以onApplicationEvent方法上加@Aync支持异步或在有@EventListener的注解方法上加上@Aync

    源码实战

    • 创建事件
    public class ProductEvent extends ApplicationEvent {
        public ProductEvent(Product product) {
            super(product);
        }
    }
    • 发布事件
    @Service
    public class ProductServiceImpl implements IproductService {
        ...
        @Autowired
        private ApplicationEventPublisher publisher;
        
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveProduct(Product product) {
            productMapper.saveProduct(product);    
            //事件发布
            publisher.publishEvent(product);
        }
        ...
    }
    • 事件监听
    @Slf4j
    @AllArgsConstructor
    public class ProductListener {
    
        private final NotifyService notifyServcie;
    
        @Async
        @Order
        @EventListener(ProductEvent.class)
        public void notify(ProductEvent event) {
            Product product = (Product) event.getSource();
            notifyServcie.notify(product, "product");
        }
    }
    • 在SpringBoot启动类上增加@EnableAsync 注解
    @Slf4j
    @EnableSwagger2
    @SpringBootApplication
    @EnableAsync
    public class ApplicationBootstrap {
    ...
    }

    注意:使用了Async后会使用默认的线程池SimpleAsyncTaskExecutor,一般我们会在项目中自定义一个线程池。

  • 相关阅读:
    POJ 1330:Nearest Common Ancestors【lca】
    图论中一类问题的总结 :必须边(点) 可行边(点)
    POJ 1486 Sorting Slides【二分图匹配】
    POJ 2375 Cow Ski Area【tarjan】
    Unity打开AppStore进行评论
    Unity3D UGUI不规则图片点击事件处理
    Unity3D之聊天框怎么跟随内容大小而变换
    Unity3D之小物体层消隐技术
    Unity3D之新手引导(责任链模式)
    Unity3D之FSM有限状态机
  • 原文地址:https://www.cnblogs.com/47Gamer/p/15817205.html
Copyright © 2011-2022 走看看