SpringBoot中使用SpringEvent业务解耦神器实现监听发布事件同步异步执行任务

场景

SpringBoot中使用单例模式+ScheduledExecutorService实现异步多线程任务(若依源码学习):

SpringBoot中使用单例模式+ScheduledExecutorService实现异步多线程任务(若依源码学习)-CSDN博客

设计模式-观察者模式在Java中的使用示例-环境监测系统:

设计模式-观察者模式在Java中的使用示例-环境监测系统_java observer使用-CSDN博客

开发过程中,业务逻辑可能非常复杂,核心业务 + N个子业务。

如果都放到一块儿去做,代码可能会很长,耦合度不断攀升。

还有一些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。

MQ 可以解决这个问题,但 MQ 重,非必要不提升架构复杂度。

针对这些问题,我们了解一下 Spring Event。

Spring Event(Application Event)其实就是一个观察者设计模式,

一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。

注:

博客:
霸道流氓气质-CSDN博客

实现

1、以下订单时同步校验订单价格和异步发送邮件通知为例

通过使用时间发布订阅的形式进行解耦。

同步校验订单价格实现

首先自定义事件,该事件携带订单id

public class OrderProductEvent {private String orderId;public OrderProductEvent(String orderId){this.orderId = orderId;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}
}

2、定义监听器

使用@EventListener注解监听并处理事件

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;@Component
public class OrderProductListener {@EventListener(OrderProductEvent.class)public void checkPrice(OrderProductEvent event){String orderId = event.getOrderId();System.out.println("校验订单价格开始:"+ LocalDateTime.now());try {TimeUnit.SECONDS.sleep(2);System.out.println("校验订单:"+orderId+"价格完成"+LocalDateTime.now());} catch (InterruptedException e) {e.printStackTrace();}}
}

3、定义发布者

通过applicationEventPublisher.publishEvent发布事件

首先新建订单Service接口

public interface OrderService {String buyOrder(String orderId);
}

接口实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;@Service
public class OrderServiceImpl implements OrderService{@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;@Overridepublic String buyOrder(String orderId) {//其它业务省略//校验订单价格-同步进行applicationEventPublisher.publishEvent(new OrderProductEvent(orderId));return "下单成功";}
}

4、编写单元测试

import lombok.SneakyThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;@RunWith(SpringRunner.class)
@SpringBootTest(classes = RuoYiApplication.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringEventTest {@Autowiredprivate OrderService orderService;@Test@SneakyThrowspublic void getDictLable() {System.out.println("下订单开始:"+ LocalDateTime.now());String s = orderService.buyOrder("0001");System.out.println(s);//执行其他业务TimeUnit.SECONDS.sleep(5);System.out.println("下订单结束:"+ LocalDateTime.now());}
}

5、单元测试运行结果

6、Spring Event 异步实现

有些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。

自定义发送邮件事件

import lombok.AllArgsConstructor;@AllArgsConstructor
public class MsgEvent {public String orderId;public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}
}

7、定义监听器

import lombok.SneakyThrows;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;@Component
public class MsgListener {@Async@SneakyThrows@EventListener(MsgEvent.class)public void sendMsg(MsgEvent event){String orderId = event.getOrderId();System.out.println("订单"+orderId+"开始发送邮件"+ LocalDateTime.now());TimeUnit.SECONDS.sleep(2);System.out.println("订单"+orderId+"发送邮件完成"+ LocalDateTime.now());}
}

需要添加@Async注解

并且需要在启动类上添加@EnableAsync注解

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@EnableAsync
public class RuoYiApplication
{public static void main(String[] args){SpringApplication.run(RuoYiApplication.class, args);
}

8、同样在上面发布者中添加发布事件

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;@Service
public class OrderServiceImpl implements OrderService{@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;@Overridepublic String buyOrder(String orderId) {//其它业务省略//校验订单价格-同步进行applicationEventPublisher.publishEvent(new OrderProductEvent(orderId));//发送邮件-异步进行applicationEventPublisher.publishEvent(new MsgEvent(orderId));return "下单成功";}
}

9、单元测试同上,运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/614209.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPT实战系列-简单聊聊LangChain

GPT实战系列-简单聊聊LangChain LLM大模型相关文章: GPT实战系列-ChatGLM3本地部署CUDA111080Ti显卡24G实战方案 GPT实战系列-Baichuan2本地化部署实战方案 GPT实战系列-大话LLM大模型训练 GPT实战系列-探究GPT等大模型的文本生成 GPT实战系列-Baichuan2等大模…

SpringBoot中使用LocalDateTime踩坑记录

文章目录 前言一、为什么推荐使用java.time包的LocalDateTime而不是java.util的Date?二、使用LocalDateTime和LocalDate时遇到了哪些坑?2.1 Redis序列化报错2.1.1 问题现象2.1.2 问题分析2.1.3 解决方案 2.2 LocalDateTime和LocalDate类型的属性返回给前…

类型特质和静态断言

static_assert( constant-expression, string-literal );static_assert( constant-expression ); // C17 (Visual Studio 2017 and later) constant-expression 可以转换为布尔值的整型常量表达式。 如果计算出的表达式为零 (false),则显示 string-literal 参数&…

PyTorch|view(),改变张量维度

在构建自己的网络时,了解数据经过每个层后的形状变化是必须的,否则,网络大概率会出现问题。PyToch张量有一个方法,叫做view(),使用这个方法,我们可以很容易的对张量的形状进行改变,从而符合网络的输入要求。…

React 18中新钩子 useDeferredValue 使用

React是一个流行的用于构建用户界面的JavaScript库,它不断发展以为开发人员提供优化性能的工具。 React 18中引入的此类工具之一是useDeferredValue钩子,它旨在通过优先渲染更新来提高应用程序的性能。 useDeferredValue钩子是什么? useDeferredValue钩子是React性能优化工…

SAP PP配置学习(五)

查找 四、 其它 设置 MM 过帐号码范围 定义凭证号码范围 OB52 打开期间 MMPV 开帐 (下篇见)

K-【学习Diffusers 四】 读取模型参数 bin格式、safetensors格式

该操作多用于推理 safetensors格式的参数读取方法 1 拿到pipeline中的unet的办法 unet pipeline.pipe.unet 2 safetensors格式文件的参数读取方法 state_dict safetensors.torch.load_file(args.model_id, device"cpu") unet.load_state_dict(state_dict) # 读入…

随心玩玩(十二)通义千问——LLM大模型微调

写在前面:使劲的摸鱼,摸到的鱼才是自己的~ 文章目录 简介环境配置模型加载jupyter远程配置快速使用微调示例部署方案总结附录: ReAct Prompting 示例准备工作一:样例问题、样例工具准备工作二:ReAct 模版步骤一&#x…

MySQL:DML数据操作语言(添加,删除,修改),DDL数据查询语言(条件查询,分组查询,排序查询,分页查询)

目录 1.DML(数据操作语言)1.添加数据2.修改数据3.删除数据 2.DQL(数据查询语言)1.DQL-语法2.基本查询3.条件查询(WHERE)1.语法:2.条件:3.案例: 4.聚合函数1.介绍2.常见聚合函数3.语法4.案例 5.分组查询(GROUP BY&#…

物联网通讯协议NB-lot和LoRa差异分析

像把大象装冰箱一样,物联网,万物互联也是要分步骤的。 一、感知层(信息获取层),即利用各种传感器等设备随时随地获取物体的信息; 二、网络层(信息传输层),通过各种电信网络与互联网的融合,将物体的信息实时准确地传递…

力扣LCR 166. 珠宝的最高价值(java 动态规划)

Problem: LCR 166. 珠宝的最高价值 文章目录 解题思路思路解题方法复杂度Code 解题思路 思路 改题目与本站64题实质上是一样的,该题目在64题的基础上将求取最小路径和改成了求取最大路径和。具体实现思路如下: 1.定义一个int类型的二维数组dp大小为给定…

17- Echarts 配置系列之:单轴 singleAxis

singleAxis: 用于展示只有一个数据维度的数据。它通常用于展示时间序列数据或者数值序列数据。 对于单轴的应用和绘制,其实就相当于我们平时的直角坐标系少一个 X 或者 Y ,然后进行图形绘制。 注意: 1.在使用单轴时&#xff0…

1.10号io网络

信号量(信号灯集) 1> 信号灯集主要完成进程间同步工作,将多个信号灯,放在一个信号灯集中,每个信号灯控制一个进程 2> 每个灯维护了一个value值,当value值等于0时,申请该资源的进程处于阻…

【Python学习】Python学习13-日期和时间

目录 【Python学习】Python学习13-日期和时间 前言通过time 获取时间戳时间元组获取当前时间,格式化时间格式化时间转换python中时间日期格式化符号获取日历Time 模块日历(Calendar)模块其他模块参考 文章所属专区 Python学习 前言 本章节主…

小马识途:十个营销故事 启发营销思路

在营销过程中,优势是相对的,只有凭借着客观的营销环境创造优势才能够取胜市场。在企业营销中,狗猛酒酸的案例比比皆是。接下来,就与小马识途一起来看看十个经典的营销故事吧! 一、摩托车公司 有家德国摩托车公司&…

SQL优化小技巧

在表中建⽴索引,优先考虑 where group by 使⽤到的字段。 查询时尽量避免使⽤select * ,只查询需要⽤到的字段。 避免在where⼦句中使⽤关键字两边都是%的模糊查询,尽量在关键字后使⽤模糊查询。 尽量避免在where⼦句中使⽤IN 和NOT IN。 …

【排序】快速排序(C语言实现)

文章目录 前言1. Hoare思想2. 挖坑法3. 前后指针法4. 三路划分5. 快速排序的一些小优化5.1 三数取中常规的三数取中伪随机的三数取中 5.2 小区间优化 6. 非递归版本的快排7. 快速排序的特性总结 前言 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其…

【开源项目】轻量元数据管理解决方案——Marquez

大家好,我是独孤风。 又到了本周的开源项目推荐。最近推荐的元数据管理项目很多,但是很多元数据管理平台的功能复杂难用。 那么有没有轻量一点的元数据管理项目呢? 今天为大家推荐的开源项目,就是一个轻量级的元数据管理工具。虽然…

Linux入门攻坚——12、Linux网络属性配置相关知识2

CentOS 7网络属性配置: 传统命名机制:以太网eth[0,1,2,...],wlan[0,1,2...] 可预测功能的命名机制: udev支持多种不同的命名方案: Firmware ,拓扑结构 在对待设备文件这块,Linux改…

人生重开模拟器(c语言)

前言: 人生重开模拟器是前段时间非常火的一个小游戏,接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏: 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 (1) 游戏开始的时…