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.hqwc.cn/news/341051.html

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

相关文章

windbg下载安装傻瓜式教程

前言 windbg是分析windows上面分析程序的利器 ,Windbg是微软出品的强大调试器,是分析软件异常的利器,Windbg之于windows就像GDB之于linux。(好了,我也就不讲废话了,我想你也只想知道最简单的安装方式...&am…

线性代数基础【4】线性方程组

第四章 线性方程组 一、线性方程组的基本概念与表达形式 二、线性方程组解的基本定理 定理1 设A为mXn矩阵,则 (1)齐次线性方程组AX0 只有零解的充分必要条件是r(A)n; (2)齐次线性方程组AX0 有非零解(或有无数个解)的充分必要条件是r(A)<n 推论1 设A为n阶矩阵,则…

你为什么还在用Promise.all?

请停止在JavaScript中使用Promise.all() 什么是JavaScript中的Promise 如果您偶然发现这篇文章,那么您可能已经熟悉了promise。 但是,对于那些JavaScript新手来说,让我们来详细说明一下。 从本质上讲,Promise对象表示异步操作的最终完成或失败。 有趣的是,当创建promise时,其值…

halcon 标定板像素当量的标定

背景:当镜头不是远心镜头时,FA镜头没法知道一个像素的尺寸。 1、标定板信息 标定板7*7,圆的直径是1.25mm,两个圆的距离是2.5mm,求出每排两两圆心距的像素距离,然后平均值。两点的真实距离为D,…

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent

文章目录 Pre概述Code源码分析 Pre Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent 概述 Spring Boot 的广播机制是基于观察者模式实现的,它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦&#…

Tensorflow2.0笔记 - Tensor的数据索引和切片

主要涉及的了基础下标索引"[]",逗号",",冒号":",省略号"..."操作,以及gather,gather_nd和boolean_mask的相关使用方法。 import tensorflow as tf import numpy as nptf.__version__tensor tf.random.uniform([1,5,5,3],…

电脑可以连接网络但浏览器无法访问部分或全部网页

啾咪!离大谱了,电脑一段时间没有用,最近打开却发现可以连接网络但是无法访问部分网页(如CSDN),显示如下: 有三种解决方法: (1)清除DNS缓存 步骤:…

归并排序例题——逆序对的数量

做道简单一点的题巩固一下 归并排序实现步骤 将整个区间 [l, r] 划分为 [l, mid] 和 [mid1, r]。 递归排序 [l, mid] 和 [mid1, r]。 将左右两个有序序列合并为一个有序序列。 题目描述 给定一个长度为 n 的整数数列,请计算数列中的逆序对的数量。 逆序对的定义…

基础篇_开发命令行程序(输入输出,类型、变量、运算符,条件语句,循环语句,方法,package与jar)

文章目录 一. 输入输出1. System.out2. System.in3. Scanner4. 变量名5. 关键字 二. 类型、变量、运算符1. 字符与字符串字符值与字符串值转义字符文本块 2. 类型何为类型数字类型字符类型 3. 变量与运算符变量运算符 4. 练习 - 房贷计算器Math.pow()数字格式化查阅 Javadoc 三…

git 的安装

git 的安装 在我们开始使用 Git 前,需要将它安装在我们的电脑上。即便已经安装,最好将它升级到最新的版本。 我们可以通过软件包或者其它安装程序来安装,或者下载源码编译安装。 本文只介绍通过在 windows 上安装软件包的方式,其…

每日一题——LeetCode1103.分糖果 ||

方法一 个人方法: 有多少人就创建多大的数组并把数组的所有元素初始化为0,只要还有糖果,就循环给数组从头到尾添加糖果,每次分的糖果数递增1,最后可能刚好分完也可能不够,不够就还剩多少给多少。 var dis…

Maven和MyBatis框架简单实现数据库交互

MyBatis是一种基于Java语言的持久层框架,它的主要目的是简化与数据库的交互过程。MyBatis通过XML或注解配置来映射Java对象和数据库表之间的关系,并提供了灵活的查询方式和结果集处理机制。MyBatis还提供了事务管理、缓存机制、插件扩展等特性。 使用My…