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 Cache和EhCache实现缓存管理方式
Jun 15 Java/Android
Java各种比较对象的方式的对比总结
Jun 20 Java/Android
Java 中的 Unsafe 魔法类的作用大全
Jun 26 Java/Android
一篇文章带你复习java知识点
Jun 28 Java/Android
SpringBoot集成Druid连接池连接MySQL8.0.11
Jul 02 Java/Android
Java网络编程之UDP实现原理解析
Sep 04 Java/Android
Java 实现限流器处理Rest接口请求详解流程
Nov 02 Java/Android
MybatisPlus EntityWrapper如何自定义SQL
Mar 22 Java/Android
Java中Quartz高可用定时任务快速入门
Apr 03 Java/Android
Java 通过手写分布式雪花SnowFlake生成ID方法详解
Apr 07 Java/Android
Java 多态分析
Apr 26 Java/Android
Spring中bean集合注入的方法详解
Jul 07 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数据库抽象层 PDO
2011/05/07 PHP
PHP 第二节 数据类型之数值型
2012/04/28 PHP
简单实现限定phpmyadmin访问ip的方法
2013/03/05 PHP
如何使用纯PHP实现定时器任务(Timer)
2015/07/31 PHP
PHP XML和数组互相转换详解
2016/10/26 PHP
解决laravel 5.1报错:No supported encrypter found的办法
2017/06/07 PHP
jquery 页面滚动到指定DIV实现代码
2013/09/25 Javascript
js使用onmousemove和onmouseout获取鼠标坐标的方法
2015/03/31 Javascript
js动态生成Html元素实现Post操作(createElement)
2015/09/14 Javascript
jQuery实现彩带延伸效果的网页加载条loading动画
2015/10/29 Javascript
JS实现获取键盘按下的按键并显示在页面上的方法
2015/11/04 Javascript
AngularJS中的Directive实现延迟加载
2016/01/25 Javascript
js正则表达式最长匹配(贪婪匹配)和最短匹配(懒惰匹配)用法分析
2016/12/27 Javascript
nodejs搭建本地服务器并访问文件的方法
2017/03/03 NodeJs
在使用JSON格式处理数据时应该注意的问题小结
2017/05/20 Javascript
移动端如何用下拉刷新的方式实现上拉加载
2018/12/10 Javascript
JS控制只能输入数字并且最多允许小数点两位
2019/11/24 Javascript
python抓取京东价格分析京东商品价格走势
2014/01/09 Python
Python兔子毒药问题实例分析
2015/03/05 Python
Python读取指定目录下指定后缀文件并保存为docx
2017/04/23 Python
Python在图片中添加文字的两种方法
2017/04/29 Python
Python数据可视化库seaborn的使用总结
2019/01/15 Python
Django 源码WSGI剖析过程详解
2019/08/05 Python
python利用7z批量解压rar的实现
2019/08/07 Python
python操作yaml说明
2020/04/08 Python
Django Auth用户认证组件实现代码
2020/10/13 Python
python跨文件使用全局变量的实现
2020/11/17 Python
Python调用Redis的示例代码
2020/11/24 Python
使用豆瓣源来安装python中的第三方库方法
2021/01/26 Python
环境工程大学生自荐信
2013/10/21 职场文书
最新的互联网创业计划书
2014/01/10 职场文书
党支部组织生活会整改方案
2014/09/30 职场文书
2016寒假假期总结
2015/10/10 职场文书
Apache Calcite 实现方言转换的代码
2021/04/24 Servers
如何用python清洗文件中的数据
2021/06/18 Python
用Python编写简单的gRPC服务的详细过程
2021/07/04 Python