Spring Event 快速入门

请直接看原文 :

Spring Event,贼好用的业务解耦神器! (qq.com)

-------------------------------------------------------------------------------------------------------------------------------- 

  • 前言

  • Spring Event 同步使用

  • Spring Event 异步使用

前言

实际业务开发过程中,业务逻辑可能非常复杂,核心业务 + N 个子业务。如果都放到一块儿去做,代码可能会很长,耦合度不断攀升,维护起来也麻烦,甚至头疼。还有一些业务场景不需要在一次请求中同步完成,比如邮件发送、短信发送等。

 MQ 确实可以解决这个问题,但 MQ 重啊,非必要不提升架构复杂度。针对这些问题,我们了解一下 Spring Event。

Spring Event 同步使用

Spring Event(Application Event)其实就是一个观察者设计模式,一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。

Spring Event 用来解耦业务真的贼好用!

| 自定义事件

定义事件,继承 ApplicationEvent 的类成为一个事件类:

import lombok.Data;
import lombok.ToString;
import org.springframework.context.ApplicationEvent;/*** @author Strive* @date 2022/4/22 18:00* @description*/
@Data
@ToString
public class OrderProductEvent extends ApplicationEvent {/** 该类型事件携带的信息 */private String orderId;public OrderProductEvent(Object source, String orderId) {super(source);this.orderId = orderId;}
}
| 定义监听器

监听并处理事件,实现 ApplicationListener 接口或者使用 @EventListener 注解:

import com.csp.mingyue.event.events.OrderProductEvent;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;/*** 实现 ApplicationListener 接口,并指定监听的事件类型** @author Strive* @date 2022/4/24 09:09* @description*/
@Slf4j
@Component
public class OrderProductListener implements ApplicationListener<OrderProductEvent> {/** 使用 onApplicationEvent 方法对消息进行接收处理 */@SneakyThrows@Overridepublic void onApplicationEvent(OrderProductEvent event) {String orderId = event.getOrderId();long start = System.currentTimeMillis();Thread.sleep(2000);long end = System.currentTimeMillis();log.info("{}:校验订单商品价格耗时:({})毫秒", orderId, (end - start));}
}
| 定义发布者

发布事件,通过 ApplicationEventPublisher 发布事件:

import com.csp.mingyue.event.events.OrderProductEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;/*** @author Strive* @date 2022/4/24 09:25* @description*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {/** 注入ApplicationContext用来发布事件 */private final ApplicationContext applicationContext;/*** 下单** @param orderId 订单ID*/public String buyOrder(String orderId) {long start = System.currentTimeMillis();// 1.查询订单详情// 2.检验订单价格 (同步处理)applicationContext.publishEvent(new OrderProductEvent(this, orderId));// 3.短信通知(异步处理)long end = System.currentTimeMillis();log.info("任务全部完成,总耗时:({})毫秒", end - start);return "购买成功";}
}

 | 单测执行

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;/*** @author Strive* @date 2022/4/24 09:28* @description*/
@SpringBootTest
public class OrderServiceTest {@Autowired private OrderService orderService;@Testpublic void buyOrderTest() {orderService.buyOrder("732171109");}
}

执行结果如下:

2022-04-24 10:13:17.535  INFO 44272 --- [           main] c.c.m.e.listener.OrderProductListener    : 732171109:校验订单商品价格耗时:(2008)毫秒
2022-04-24 10:13:17.536  INFO 44272 --- [           main] c.c.mingyue.event.service.OrderService   : 任务全部完成,总耗时:(2009)毫秒

 Spring Event 异步使用

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

| 自定义事件
import lombok.AllArgsConstructor;
import lombok.Data;/*** @author Strive* @date 2022/4/24 10:18* @description*/
@Data
@AllArgsConstructor
public class MsgEvent {/** 该类型事件携带的信息 */public String orderId;
}
| 定义监听器

推荐使用 @EventListener 注解:

import com.csp.mingyue.event.events.MsgEvent;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;/*** @author Strive* @date 2022/4/24 10:20* @description*/
@Slf4j
@Component
public class MsgListener {@SneakyThrows@EventListener(MsgEvent.class)public void sendMsg(MsgEvent event) {String orderId = event.getOrderId();long start = System.currentTimeMillis();log.info("开发发送短信");log.info("开发发送邮件");Thread.sleep(4000);long end = System.currentTimeMillis();log.info("{}:发送短信、邮件耗时:({})毫秒", orderId, (end - start));}
}
| 定义发布者
/*** 下单** @param orderId 订单ID*/
public String buyOrder(String orderId) {long start = System.currentTimeMillis();// 1.查询订单详情// 2.检验订单价格 (同步处理)applicationContext.publishEvent(new OrderProductEvent(this, orderId));// 3.短信通知(异步处理)applicationContext.publishEvent(new MsgEvent(orderId));long end = System.currentTimeMillis();log.info("任务全部完成,总耗时:({})毫秒", end - start);return "购买成功";
}

| 单测执行(同步)

@Test
public void buyOrderTest() {orderService.buyOrder("732171109");
}

执行结果如下:

2022-04-24 10:24:13.905  INFO 54848 --- [           main] c.c.m.e.listener.OrderProductListener    : 732171109:校验订单商品价格耗时:(2004)毫秒
2022-04-24 10:24:13.906  INFO 54848 --- [           main] c.c.mingyue.event.listener.MsgListener   : 开发发送短信
2022-04-24 10:24:13.907  INFO 54848 --- [           main] c.c.mingyue.event.listener.MsgListener   : 开发发送邮件
2022-04-24 10:24:17.908  INFO 54848 --- [           main] c.c.mingyue.event.listener.MsgListener   : 732171109:发送短信、邮件耗时:(4002)毫秒
2022-04-24 10:24:17.908  INFO 54848 --- [           main] c.c.mingyue.event.service.OrderService   : 任务全部完成,总耗时:(6008)毫秒
| 开启异步

启动类增加 @EnableAsync 注解:

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

Listener 类需要开启异步的方法增加 @Async 注解:

@Async
@SneakyThrows
@EventListener(MsgEvent.class)
public void sendMsg(MsgEvent event) {String orderId = event.getOrderId();long start = System.currentTimeMillis();log.info("开发发送短信");log.info("开发发送邮件");Thread.sleep(4000);long end = System.currentTimeMillis();log.info("{}:发送短信、邮件耗时:({})毫秒", orderId, (end - start));
}
| 单测执行(异步)

发送短信的线程显示 task-1,主线程结束后(总耗时:(2017)毫秒)控制台停止打印了:

2022-04-24 10:30:59.002  INFO 59448 --- [           main] c.c.m.e.listener.OrderProductListener    : 732171109:校验订单商品价格耗时:(2009)毫秒
2022-04-24 10:30:59.009  INFO 59448 --- [           main] c.c.mingyue.event.service.OrderService   : 任务全部完成,总耗时:(2017)毫秒
2022-04-24 10:30:59.028  INFO 59448 --- [         task-1] c.c.mingyue.event.listener.MsgListener   : 开发发送短信
2022-04-24 10:30:59.028  INFO 59448 --- [         task-1] c.c.mingyue.event.listener.MsgListener   : 开发发送邮件

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

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

相关文章

ADAS智能驾驶测试知多少?

当涉及ADAS&#xff08;Advanced Driver Assistance Systems&#xff09;智能驾驶的测试时&#xff0c;有一个完整的测试体系可以用来评估系统的性能和功能。 1. 传感器测试 1.1 传感器校准测试 描述&#xff1a;确保传感器&#xff08;如雷达、摄像头、激光雷达等&#xff09;…

【stm32】hal库学习笔记-UART/USART串口通信(超详细!)

【stm32】hal库学习笔记-UART/USART串口通信 hal库驱动函数 CubeMX图形化配置 导入LCD.ioc RTC设置 时钟树配置 设置LSE为RTC时钟源 USART设置 中断设置 程序编写 编写主函数 /* USER CODE BEGIN 2 */lcd_init();lcd_show_str(10, 10, 16, "Demo12_1:USART1-CH340&q…

Java中各种O(PO,BO,DTO,VO等) 是不是人为增加系统复杂度?

Java中各种O(PO,BO,DTO,VO等) 是不是人为增加系统复杂度&#xff1f; 在Java和其他编程语言的开发过程中&#xff0c;经常会用到几个以"O"结尾的缩写&#xff0c;比如PO,BO,DTO,VO等等&#xff0c;O在这里是Object的缩写&#xff0c;不同的O代表了不同的数据类型&am…

Java基础 - 13 Queue之DelayQueue、PriorityQueue、PriorityBlockingQueue讲解

在Java的队列世界里&#xff0c;有三位大佬&#xff0c;他们分别是DelayQueue、PriorityQueue和PriorityBlockingQueue。今天&#xff0c;让我们一起揭开他们神秘的面纱&#xff0c;看看他们各自的特点和用途吧&#xff01; DelayQueue 首先&#xff0c;让我们来认识一下Delay…

2.22 作业

顺序表 运行结果 fun.c #include "fun.h" seq_p create_seq_list() {seq_p L (seq_p)malloc(sizeof(seq_list));if(LNULL){printf("空间申请失败\n");return NULL;}L->len 0; bzero(L,sizeof(L->data)); return L; } int seq_empty(seq_p L) {i…

工厂方法模式Factory Method

1.模式定义 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。Factory Method 使得一个类的实例化延迟到子类 2.使用场景 1.当你不知道改使用对象的确切类型的时候 2.当你希望为库或框架提供扩展其内部组件的方法时 主要优点&#xff1a; 1.将具体产品和创建…

PHP+vue+mysql仓库进销存管理系统8jeqj

开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 运行环境:phpstudy/wamp/xammp等 数据库工具&#xff1a;Navicat/phpmyadmin 登录模块&#xff1a;此模块主要有4个部分&#xff0c;基…

实验中用户行为的检测

[1] Makris S , Karagiannis P , Koukas S ,et al.Augmented reality system for operator support in human–robot collaborative assembly[J].CIRP Annals - Manufacturing Technology, 2016:S0007850616300385.DOI:10.1016/j.cirp.2016.04.038. —————————————…

什么是SSD型云服务器?

​  SSD云服务器是一种使用固态硬盘代替传统HDD进行存储的虚拟机。SDD 使用闪存单元来存储数据&#xff0c;与云计算技术相结合&#xff0c;形成强大且高效的存储解决方案&#xff0c;可以随时随地访问。 SSD云服务器如何工作? SSD云服务器是利用虚拟化和云计算技术创建的。…

C++最佳实践之编译篇

C最佳实践之工程编译 在大型c/c工程开发中&#xff0c;往往会涉及多级CMakeLists.txt的调用&#xff0c;并且调用方式错综复杂&#xff0c;主要有以下两种方式&#xff1a; 1. 子目录中的CMakeList.txt独立生成目标&#xff0c;不作为主目标生成过程的依赖关系&#xff08;比…

virtualenv env_name 使用 virtualenv 创建 python 虚拟环境

为什么要用这个 win7 32 环境下 pycharm 只能用低版本的&#xff0c;比如 2016,2018 此时pycharm 图形界面创建的 虚拟环境版本很低&#xff0c;有些包不兼容&#xff0c;因此用 virtualenv 模块&#xff0c;可以创建 20 版本以上的虚拟环境 virtualenv env_name官方文档 http…

如何选择最适合的图纸加密软件?安秉网盾软件用户体验及性价比

安秉网盾图纸加密软件是一款功能强大的图纸加密工具&#xff0c;具有以下特点和优势&#xff1a; 全盘加密&#xff1a;安秉网盾采用先进的加密算法&#xff0c;能对文件、文件夹、磁盘等数据进行全面加密&#xff0c;确保数据在存储和传输过程中的安全性。 监控与审计&#x…

关于el-select值的回显问题 : 框内显示label值还是value值

<el-form-item label"状态" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"请选择状态"value-key"value"//value-key 与下面的ke…

How to implement multiple file uploads based on Swagger 3.x in Spring boot 3.x

How to implement multiple file uploads based on Swagger 3.x in Spring boot 3.x Projectpom.xmlOpenAPIConfigFileUploadControllerapplication.yaml Project pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://…

软件压力测试:测试方法与步骤详解

随着软件应用的不断发展&#xff0c;用户对系统性能的要求也逐渐提高。在不同的负载条件下&#xff0c;系统必须能够保持稳定、高效的运行。软件压力测试是一种验证系统在各种负载情况下性能表现的关键手段。本文将详细探讨软件压力测试的方法和步骤。 1. 明确测试目标 在进行压…

vue源码分析之nextTick源码分析-逐行逐析-错误分析

nextTick的使用背景 在vue项目中&#xff0c;经常会使用到nextTick这个api&#xff0c;一直在猜想其是怎么实现的&#xff0c;今天有幸研读了下&#xff0c;虽然源码又些许问题&#xff0c;但仍值得借鉴 核心源码解析 判断当前环境使用最合适的API并保存函数 promise 判断…

2024年2月17日~2月23日周报

文章目录 一、前言二、DDNet架构学习2.1 数据预处理2.2 网络模型构建 三、基于深度学习地震数据去噪处理3.1 深度学习在地震数据去噪中的研究方向3.2 深度学习地震数据去噪流程3.2.1 数据集准备3.2.2 模型构建3.2.3 训练网络 3.3 基于DnCNN的地震数据去噪实验 四、小结4.1 存在…

Javascript数字精度丢失的问题

一、问题 0.1 0.2 0.3 // false 二、浮点数 “浮点数”是一种表示数字的标准&#xff0c;整数也可以用浮点数的格式来存储 我们也可以理解成&#xff0c;浮点数就是小数 在JavaScript中&#xff0c;现在主流的数值类型是Number&#xff0c;而Number采用的是IEEE754规范中…

uniapp开发微信小程序跳转到另一个小程序中

注意&#xff1a;一开始我的云上务工模块是单独的tabbar界面&#xff0c;但是小程序跳转好像不能直接点击tabbar进行&#xff0c;所以我将这里改成了点击首页中的按钮进行跳转 点击这里进行小程序跳转 目录 基础讲解 uniapp小程序跳转的两个方法 调用说明&#xff08;半屏跳转…

C++力扣题目 647--回文子串 516--最长回文子序列

647. 回文子串 力扣题目链接(opens new window) 给定一个字符串&#xff0c;你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0c;也会被视作不同的子串。 示例 1&#xff1a; 输入&#xff1a…