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 16 Java/Android
启动Tomcat时出现大量乱码的解决方法
Jun 21 Java/Android
Java Socket实现多人聊天系统
Jul 15 Java/Android
简述Java中throw-throws异常抛出
Aug 07 Java/Android
springboot如何接收application/x-www-form-urlencoded类型的请求
Nov 02 Java/Android
java executor包参数处理功能 
Feb 15 Java/Android
Java实现学生管理系统(IO版)
Feb 24 Java/Android
Java 超详细讲解IO操作字节流与字符流
Mar 25 Java/Android
Java实战之课程信息管理系统的实现
Apr 01 Java/Android
Spring Boot 实现 WebSocket
Apr 30 Java/Android
SpringBoot详解自定义Stater的应用
Jul 15 Java/Android
Spring boot admin 服务监控利器详解
Aug 05 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
VFP与其他应用程序的集成
2006/10/09 PHP
PHP中替换换行符的几种方法小结
2012/10/15 PHP
做了CDN获取用户真实IP的函数代码(PHP与Asp设置方式)
2013/04/13 PHP
3个PHP多维数组转为一维数组的方法实例
2014/03/13 PHP
PHP面向对象程序设计之接口用法
2014/08/20 PHP
php+html5使用FormData对象提交表单及上传图片的方法
2015/02/11 PHP
PHP单例模式详解及实例代码
2016/12/21 PHP
JS 文件本身编码转换 图文教程
2009/10/12 Javascript
javascript qq右下角滑出窗口 sheyMsg
2010/03/21 Javascript
javascript textContent与innerText的异同分析
2010/10/22 Javascript
Javascript 键盘事件的组合使用实现代码
2012/05/04 Javascript
自己编写的类似JS的trim方法
2013/10/09 Javascript
jquery基础教程之deferred对象使用方法
2014/01/22 Javascript
详解JavaScript节流函数中的Throttle
2016/07/16 Javascript
javascript数组去重方法分析
2016/12/15 Javascript
JS图片轮播与索引变色功能实例详解
2017/07/06 Javascript
nodejs实现解析xml字符串为对象的方法示例
2018/03/14 NodeJs
JS使用tween.js动画库实现轮播图并且有切换功能
2018/07/17 Javascript
Vue弹出菜单功能的实现代码
2018/09/12 Javascript
解决微信小程序调用moveToLocation失效问题【超简单】
2019/04/12 Javascript
vue  elementUI 表单嵌套验证的实例代码
2019/11/06 Javascript
[22:07]DOTA2-DPC中国联赛 正赛 iG vs Magma 选手采访
2021/03/11 DOTA
python双向链表实现实例代码
2013/11/21 Python
python Django模板的使用方法
2016/01/14 Python
Python FTP文件定时自动下载实现过程解析
2019/11/12 Python
复化梯形求积分实例——用Python进行数值计算
2019/11/20 Python
Python实现链表反转的方法分析【迭代法与递归法】
2020/02/22 Python
Stuart Weitzman欧盟:美国奢华鞋履品牌
2017/05/24 全球购物
美国高级音响品牌:Master&Dynamic
2018/07/05 全球购物
Saks Fifth Avenue澳洲/亚太地区:萨克斯第五大道精品百货店
2019/06/09 全球购物
利物浦足球俱乐部官方商店(美国):Liverpool FC US
2019/10/09 全球购物
小学运动会入场式解说词
2014/02/18 职场文书
2014收银员工作总结范文
2014/12/16 职场文书
一年级语文下册复习计划
2015/01/17 职场文书
3招让你摆脱即兴讲话冷场尴尬
2019/08/08 职场文书
浅谈resultMap的用法及关联结果集映射
2021/06/30 Java/Android