• 在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,一般我们会在项目中自定义一个线程池。

  • 相关阅读:
    Storm应用系列之——集成Kafka
    Storm常见模式——分布式RPC
    Storm常见模式——流聚合
    博客迁移
    设计模式学习笔记
    JStorm模型设计
    MySQL学习笔记
    JAVA学习笔记
    JavaScript学习笔记
    用ACE来写代码
  • 原文地址:https://www.cnblogs.com/47Gamer/p/15817205.html
Copyright © 2020-2023  润新知