SpringBoot实现异步事件驱动的方法


Posted in Java/Android onJune 28, 2021

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

@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个部分组成

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

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

  • 自定义需要发布的事件类,需要继承ApplicationEvent类
  • 使用ApplicationEventPublisher来发布自定义事件
  • 使用@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,一般我们会在项目中自定义一个线程池。

@Configuration
public class ExecutorConfig {
    /** 核心线程数 */
    private int corePoolSize = 10;
    /** 最大线程数  */
    private int maxPoolSize = 50;
    /** 队列大小  */
    private int queueCapacity = 10;
    /** 线程最大空闲时间   */
    private int keepAliveSeconds = 150;

    @Bean("customExecutor")
    public Executor myExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("customExecutor-");
        executor.setKeepAliveSeconds(keepAliveSeconds);

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

到此这篇关于SpringBoot实现异步事件驱动的方法的文章就介绍到这了,更多相关SpringBoot 异步事件驱动内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
源码解读Spring-Integration执行过程
Jun 11 Java/Android
Java数据结构之链表相关知识总结
Jun 18 Java/Android
利用Java设置Word文本框中的文字旋转方向的实现方法
Jun 28 Java/Android
Java基础之线程锁相关知识总结
Jun 30 Java/Android
java泛型通配符详解
Jul 25 Java/Android
Java移除无效括号的方法实现
Aug 07 Java/Android
Java9新特性对HTTP2协议支持与非阻塞HTTP API
Mar 16 Java/Android
RestTemplate如何通过HTTP Basic Auth认证示例说明
Mar 17 Java/Android
springboot 全局异常处理和统一响应对象的处理方式
Jun 28 Java/Android
Android实现图片九宫格
Jun 28 Java/Android
spring 项目实现限流方法示例
Jul 15 Java/Android
Java获取字符串编码格式实现思路
Sep 23 Java/Android
Spring整合Mybatis的全过程
Jun 28 #Java/Android
Java中常用解析工具jackson及fastjson的使用
Java中使用Filter过滤器的方法
Jun 28 #Java/Android
浅谈Python魔法方法
Java实现二维数组和稀疏数组之间的转换
深入理解java.lang.String类的不可变性
springboot拦截器无法注入redisTemplate的解决方法
You might like
2.PHP入门
2006/10/09 PHP
PHP中for循环语句的几种变型
2007/03/16 PHP
php面向对象全攻略 (四)构造方法与析构方法
2009/09/30 PHP
php数组函数序列之array_sum() - 计算数组元素值之和
2011/10/29 PHP
file_get_contents("php://input", "r")实例介绍
2013/07/01 PHP
Yii学习总结之安装配置
2015/02/22 PHP
PHP常用header头定义代码示例汇总
2020/08/29 PHP
深入学习jQuery Validate表单验证(二)
2016/01/18 Javascript
Nodejs之TCP服务端与客户端聊天程序详解
2017/07/07 NodeJs
Vue2.0子同级组件之间数据交互方法
2018/02/28 Javascript
详解vue表单——小白速看
2018/04/08 Javascript
vue3.0 CLI - 2.1 -  component 组件入门教程
2018/09/14 Javascript
JavaScript中关于base64的一些事
2019/05/06 Javascript
对layui中table组件工具栏的使用详解
2019/09/19 Javascript
基于JS判断对象是否是数组
2020/01/10 Javascript
JS中的变量作用域(console版)
2020/07/18 Javascript
一起深入理解js中的事件对象
2021/02/06 Javascript
简单介绍利用TK在Python下进行GUI编程的教程
2015/04/13 Python
用python找出那些被“标记”的照片
2017/04/20 Python
OpenCV2.3.1+Python2.7.3+Numpy等的配置解析
2018/01/05 Python
python机器学习理论与实战(一)K近邻法
2021/01/28 Python
使用pandas对矢量化数据进行替换处理的方法
2018/04/11 Python
Python爬虫PyQuery库基本用法入门教程
2018/08/04 Python
WIn10+Anaconda环境下安装PyTorch(避坑指南)
2019/01/30 Python
python 日期排序的实例代码
2019/07/11 Python
用Python从0开始实现一个中文拼音输入法的思路详解
2019/07/20 Python
python json.dumps() json.dump()的区别详解
2020/07/14 Python
python 使用递归的方式实现语义图片分割功能
2020/07/16 Python
详解查看Python解释器路径的两种方式
2020/10/15 Python
python 基于opencv操作摄像头
2020/12/24 Python
浅谈Html5中视频 音频标签 进度条的问题
2016/07/26 HTML / CSS
德国baby-markt婴儿用品瑞士网站:baby-markt.ch
2017/06/09 全球购物
迪卡侬荷兰官网:Decathlon荷兰
2017/10/29 全球购物
工人先锋号事迹材料
2014/12/24 职场文书
党校个人总结
2015/03/04 职场文书
2016年清明节期间群众祭祀活动工作总结
2016/04/01 职场文书