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 相关文章推荐
Java实现斗地主之洗牌发牌
Jun 14 Java/Android
解决SpringBoot文件上传临时目录找不到的问题
Jul 01 Java/Android
Java Kafka 消费积压监控的示例代码
Jul 01 Java/Android
java设计模式--原型模式详解
Jul 21 Java/Android
SpringBoot实现quartz定时任务可视化管理功能
Aug 30 Java/Android
聊聊Lombok中的@Builder注解使用教程
Nov 17 Java/Android
解决persistence.xml配置文件修改存放路径的问题
Feb 24 Java/Android
java objectUtils 使用可能会出现的问题
Feb 28 Java/Android
Java基础——Map集合
Apr 01 Java/Android
Springboot-cli 开发脚手架,权限认证,附demo演示
Apr 28 Java/Android
Java实现字符串转为驼峰格式的方法详解
Jul 07 Java/Android
Java Redisson多策略注解限流
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
php防注入,表单提交值转义的实现详解
2013/06/10 PHP
wordpress安装过程中遇到中文乱码的处理方法
2015/04/21 PHP
Yii2.0 模态弹出框+ajax提交表单
2016/05/22 PHP
JQuery 图片延迟加载并等比缩放插件
2009/11/09 Javascript
在表单提交前进行验证的几种方式整理
2013/07/31 Javascript
一个JavaScript函数把URL参数解析成Json对象
2014/09/24 Javascript
纯HTML5制作围住神经猫游戏-附源码下载
2015/08/23 Javascript
Javascript中的Prototype到底是什么
2016/02/16 Javascript
JS在一定时间内跳转页面及各种刷新页面的实现方法
2016/05/26 Javascript
jQuery实现立体式数字滚动条增加效果
2016/12/21 Javascript
js实现前端图片上传即时预览功能
2017/08/02 Javascript
web前端开发中常见的多列布局解决方案整理(一定要看)
2017/10/15 Javascript
详解iframe跨域的几种常用方法(小结)
2019/04/29 Javascript
echarts多条折线图动态分层的实现方法
2019/05/24 Javascript
IE11下CKEditor在Bootstrap Modal中下拉问题的解决
2019/09/25 Javascript
[55:25]VGJ.T vs Optic Supermajor小组赛D组 BO3 第三场 6.3
2018/06/04 DOTA
[33:23]Secret vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python实现的朴素贝叶斯分类器示例
2018/01/06 Python
Django处理文件上传File Uploads的实例
2018/05/28 Python
python sqlite的Row对象操作示例
2019/09/11 Python
python3实现在二叉树中找出和为某一值的所有路径(推荐)
2019/12/26 Python
通过 Python 和 OpenCV 实现目标数量监控
2020/01/05 Python
Python实现点云投影到平面显示
2020/01/18 Python
使用CSS3的box-sizing属性解决div宽高被内边距撑开的问题
2016/06/28 HTML / CSS
Linux常见面试题
2016/10/04 面试题
编辑个人求职信范文
2013/09/21 职场文书
生产班组长岗位职责
2014/01/05 职场文书
七年级音乐教学反思
2014/01/26 职场文书
电焊工工作岗位职责
2014/02/06 职场文书
英语老师推荐信
2014/02/26 职场文书
2015年财务人员工作总结
2015/04/10 职场文书
2015年办公室个人工作总结
2015/04/20 职场文书
欠款纠纷起诉状
2015/05/19 职场文书
关于保护环境的建议书
2019/06/24 职场文书
教你怎么用Python操作MySql数据库
2021/05/31 Python
PHP中多字节字符串操作实例详解
2021/08/23 PHP