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

  • 相关阅读:
    org.springframework.transaction.CannotCreateTransactionException Could not open
    org.springframework.web.util.NestedServletException Handler processing failed; n
    SSM 后台封装的有值, 到前台打印的时候没有值
    javax.net.ssl.SSLHandshakeException sun.security.validator.ValidatorException PK
    a 链接点击下载
    form图片上传遇到错误
    forever 启动nodejs
    nodejs 新建项目
    nodejs 安装
    nodejs 爬虫模板 map&array 数据模型
  • 原文地址:https://www.cnblogs.com/47Gamer/p/15817205.html
Copyright © 2011-2022 走看看