Spring Event 快速入门

news2024/11/15 23:51:05

请直接看原文 :

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
  @Override
  public 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;

  @Test
  public 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.coloradmin.cn/o/1463691.html

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

相关文章

mac真的安装不了vmware吗 mac如何安装crossover crossover序列号从哪里买 购买正版渠道

有些用户可能想在mac上运行一些只能在windows上运行的软件&#xff0c;比如游戏、专业软件等。这时候&#xff0c;就需要用到虚拟机技术&#xff0c;也就是在mac上安装一个可以模拟其他操作系统的软件&#xff0c;比如vmware或者crossover。那么&#xff0c;mac真的安装不了vmw…

【鸿蒙 HarmonyOS 4.0】数据持久化

一、数据持久化介绍 数据持久化是将内存数据(内存是临时的存储空间)&#xff0c;通过文件或数据库的形式保存在设备中。 HarmonyOS提供两种数据持久化方案&#xff1a; 1.1、用户首选项&#xff08;Preferences&#xff09;&#xff1a; 通常用于保存应用的配置信息。数据通…

《VitePress 简易速速上手小册》第8章 安全性与部署(2024 最新版)

文章目录 8.1 安全最佳实践8.1.1 基础知识点解析8.1.2 重点案例&#xff1a;个人博客8.1.3 拓展案例 1&#xff1a;在线商店8.1.4 拓展案例 2&#xff1a;企业网站 8.2 部署到 GitHub Pages 和其他平台8.2.1 基础知识点解析8.2.2 重点案例&#xff1a;个人博客部署到 GitHub Pa…

什么是城市智慧驿站-智慧公厕?城市智慧驿站-智慧公厕建设的好处

城市智慧驿站-智慧公厕&#xff0c;作为一种信息化、景观化、模块化、智慧化的新型公共厕所&#xff0c;正逐渐成为城市环境建设中的亮点。这些智慧驿站不仅提供卫生便利的公共厕所功能&#xff0c;更是融入了创新的设计理念和智能化管理系统&#xff0c;为城市居民和游客带来全…

进度条小程序

文章目录 铺垫回车换行缓冲区概述强制冲刷缓冲区 简单实现倒计时功能进度条小程序版本一实例代码效果展示分析 版本二 铺垫 回车换行 回车和换行是两个独立的动作 回车是将光标移动到当前行的最开始&#xff08;最左侧&#xff09; 换行是竖直向下平移一行 在C语言中&…

学习总结22

解题思路 简单模拟。 代码 #include <bits/stdc.h> using namespace std; long long g[2000000]; long long n; int main() {long long x,y,z,sum0,k0;scanf("%lld",&n);for(x1;x<n;x)scanf("%lld",&g[x]);for(x1;x<n;x){scanf(&qu…

c++类和对象新手保姆级上手教学(下)

目录 前言&#xff1a; 初始化列表&#xff1a; explicit关键字&#xff1a; static成员&#xff1a; 友元函数&#xff1a; 友元类&#xff1a; 内部类&#xff1a; 匿名对象&#xff1a; 前言&#xff1a; 类和对象下篇中剩余的部分较为简单易理解&#xff0c;认真记住…

三opencv源码解压及环境变量配置

1.双击opencv-3.4.6-vc14-vc15.exe 2.选择解压的路径&#xff0c;点击【extract】 3.设计环境变量

05.STLvector、list、stack、queue

STL标准模板库 standard template library STL将原来常用的容器和操作进行封装&#xff0c;增加了C的编码效率 容器 string #include vector #include list #include stack #include queue #include set #include map #include 迭代器 容器和算法之间的粘合剂&#xff0…

【海贼王的数据航海:利用数据结构成为数据海洋的霸主】时间复杂度 | 空间复杂度

目录 1 -> 算法效率 1.1 -> 如何衡量一个算法的好坏&#xff1f; 1.2 -> 算法的复杂度 2 -> 时间复杂度 2.1 -> 时间复杂度的概念 2.2 -> 大O的渐进表示法 2.3 -> 常见时间复杂度计算 3 -> 空间复杂度 4 -> 常见复杂度对比 1 -> 算法效…

数字化转型导师坚鹏:数字政府技术、业务、数据融合发展路径探索

数字政府建设与发展研究 ——技术、业务、数据融合发展路径探索 课程背景&#xff1a; 很多政府存在以下问题&#xff1a; 不清楚数字政府建设内涵 不清楚数字政府建设现状 不清楚数字政府融合路径 课程特色&#xff1a; 有实战案例 有原创观点 有精彩解读 学…

来分析两道小题

一、源码 二、分析 首先它会接两个参数一个是id一个是ps&#xff0c;传递的话会包含一个flag.php&#xff0c;然后数据库连接&#xff0c;之后传递过滤&#xff0c;然后查询&#xff0c;如果查到了就会取id&#xff0c;取出来看是不是跟adog一样&#xff0c;如果是它告诉你账号…

Imagewheel私人图床搭建结合内网穿透实现无公网IP远程访问教程

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

四年功能测试,测试工作吐槽

这篇文章基于中小公司的工作经验&#xff0c;中小厂的测试流程往往没有大厂那么规范&#xff0c;也会遇到很多挑战&#xff0c;来看下他是怎么解决的 问题一&#xff1a;测试时间评估 这是一个工作日常经常需要回复的问题&#xff0c;理论上 测试这边要做出较科学合理的回复&…

Flutter学习4 - Dart数据类型

1、基本数据类型 num、int、double &#xff08;1&#xff09;常用数据类型 num类型&#xff0c;是数字类型的父类型&#xff0c;有两个子类 int 和 double 通过在函数名前加下划线&#xff0c;可以将函数变成私有函数&#xff0c;私有函数只能在当前文件中调用 //常用数据…

2024 财源(裁员)广进(鸿蒙开发岗)~

2023已过去了&#xff0c;2024大家都在期待经济复苏起来&#xff0c;但并没有起来&#xff0c;倒是第一波 “裁员潮”突然来袭&#xff1a; 这一波突然袭击&#xff0c;使得一些年后想换工作的人不敢轮乱动了&#xff0c;真不知道哪一天起床就失业了 今年我已经接到了太多粉丝…

力扣OJ题——随机链表的复制

题目&#xff1a; 138. 随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 要求&#xff1a;构造这个链表的 深拷贝 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中…

LabVIEW多通道压力传感器实时动态检测

LabVIEW多通道压力传感器实时动态检测 介绍了一种基于LabVIEW的多通道压力传感器实时动态检测系统&#xff0c;解决压阻式压力传感器温度补偿过程的复杂度&#xff0c;提高测量的准确性。通过自动轮询检测方法&#xff0c;结合硬件检测模型和多通道检测系统设计&#xff0c;本…

【动态规划专栏】专题三:简单多状态dp--------3.删除并获得点数

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

ios抓包Tunnel to......443

fiddler官网下载“CertMaker for iOS and Android”插件&#xff0c;官网插件&#xff1a;https://www.telerik.com/fiddler/add-ons 双击运行插件后&#xff0c;重启fiddler&#xff0c;ios重新安装证书即可