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

news2025/1/11 2:39:24

场景

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{

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

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

    @Autowired
    private OrderService orderService;

    @Test
    @SneakyThrows
    public 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{

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public String buyOrder(String orderId) {
        //其它业务省略
        //校验订单价格-同步进行
        applicationEventPublisher.publishEvent(new OrderProductEvent(orderId));
        //发送邮件-异步进行
        applicationEventPublisher.publishEvent(new MsgEvent(orderId));
        return "下单成功";
    }
}

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

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

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

相关文章

基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpResponse的定义和初始化 以及组织 HttpResponse 响应消息

一、HttpResponse的定义 1.定义状态码枚举 // 定义状态码枚举 enum HttpStatusCode {Unknown 0,OK 200,MovedPermanently 301,MovedTemporarily 302,BadRequest 400,NotFound 404 }; 2.HTTP 响应报文格式 这个数据块主要是分为四部分 第一部分是状态行第二部分是响应…

Godot之StringName解析

类描述 在Godot中,StringName是唯一字符串的内置类型。 StringName 是不可变的字符串,用于唯一名称的通用表示(也叫“字符串内嵌”)。值相同的两个 StringName 是同一个对象。进行比较时比普通 String 要快很多。 对于需要 Str…

时光总是催人老 time

文章目录 时光总是催人老 time语法示例更多信息 时光总是催人老 time … note:: 林花谢了春红,太匆匆。无奈朝来寒雨晚来风。 李煜《相见欢林花谢了春红》 Linux time命令的用途,在于测量指定命令消耗的时间。 最常用的在于大概评估一个程序的运行时…

APP流量变现——4项关键指标决定了APP混合变现的收入

APP流量变现的方式有很多种,主要的可以分为IAA(广告)收入、IAP(用户应用内付费)收入、订阅收入、单次买断收入。这里主要围绕当前流行的混合变现模式,即广告收入(IAA)应用内付费&…

vue3+vite开发生产环境区分

.env.development VITE_APP_TITLE本地.env.production VITE_APP_TITLE生产-ts文件中应用 console.log(import.meta.env.VITE_APP_TITLE)在html中应用,需要安装 html 模板插件 pnpm add vite-plugin-html -Dvite.config.ts中 import { createHtmlPlugin } from v…

鸿蒙原生应用再添新丁!万达 入局鸿蒙

鸿蒙原生应用再添新丁!万达 入局鸿蒙 来自 HarmonyOS 微博1月11日消息,#万达酒店及度假村启动鸿蒙原生应用及元服务开发# 作为具有中国特色的国牌服务酒店标杆之一,万达酒店及度假村Wanda 将带来全新的服务和交互方式,一步获取“…

【AI视野·今日Robot 机器人论文速览 第七十三期】Tue, 9 Jan 2024

AI视野今日CS.Robotics 机器人学论文速览 Tue, 9 Jan 2024 Totally 40 papers 👉上期速览✈更多精彩请移步主页 Daily Robotics Papers Digital Twin for Autonomous Surface Vessels for Safe Maritime Navigation Authors Daniel Menges, Andreas Von Brandis, A…

基于ssm运动会管理系统的设计与实现 【附源码】

基于ssm运动会管理系统的设计与实现 【附源码】 🍅 作者主页 央顺技术团队 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 文末获取源码联系方式 📝 项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuil…

Flashduty 案例分享 - 途游游戏

Flashduty 作为功能完备的事件OnCall中心,可以接入云上、云下不同监控系统,统一做告警降噪分派、认领升级、排班协同,已经得到众多先进企业的认可。我们采访了一些典型客户代表,了解他们的痛点、选型考虑和未来展望,集…

【排序算法】四、堆排序(C/C++)

「前言」文章内容是排序算法之堆排序的讲解。(所有文章已经分类好,放心食用) 「归属专栏」排序算法 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 堆排序1.1 原理1.2 堆的向下调整1.3 堆排序代码实现1.3 性质总结 堆排序 1.1 原理 概念介…

爬取去哪网旅游攻略信息

代码展现: import requests import parsel import csv import time f open(旅游去哪攻略.csv,modea,encodingutf-8,newline) csv_writer csv.writer(f) csv_writer.writerow([标题,浏览量,日期,天数,人物,人均价格,玩法]) for page in range(1,5):url fhttps://…

JS 函数

函数就是封装了一段可以被重复执行调用的代码块。目的:让大量代码重复利用 1、声明函数 方式一:利用函数关键字自定义函数(命名函数) function 函数名(){//函数体代码} function是声明函数的关键字&#…

气膜建筑的消防安全问题如何保障?

气膜建筑作为一种独特的建筑形式,拥有广泛的应用领域。然而,由于其密闭性特点,人们更加关注其消防安全问题。以下是保障气膜建筑消防安全的几个关键措施: 采用难燃材料: 气膜建筑所使用的建筑膜材采用B1级难燃材料&…

Go后端开发 -- 条件、循环语句 defer语句

Go后端开发 – 条件、循环语句 && defer语句 文章目录 Go后端开发 -- 条件、循环语句 && defer语句一、条件语句1.if ... else 语句2.switch语句3.select语句 二、循环语句1.for循环 三、defer语句1.defer语句的作用2.defer和return的先后顺序3.recover错误拦截…

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent

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

【JaveWeb教程】(19) MySQL数据库开发之 MySQL数据库操作-DML 详细代码示例讲解

目录 3. 数据库操作-DML3.1 增加(insert)3.2 修改(update)3.3 删除(delete)3.4 总结 3. 数据库操作-DML DML英文全称是Data Manipulation Language(数据操作语言),用来对数据库中表的数据记录进行增、删、改操作。 添加数据(INSERT)修改数据…

第二百六十回

文章目录 知识回顾示例代码经验总结 我们在上一章回中介绍了通道相关的内容,本章回中将介绍其中的一种通道:MethodChannnel.闲话休提,让我们一起Talk Flutter吧。 知识回顾 我们在上一章回中介绍了通道的概念和作用,并且提到了通…

脱机I/O方式和假脱机系统

提示:在写这个博客的时候小编更加的觉得计算机基础知识的重要性了,而且对计算机的整个发展历程和计算机的底层工作原理特别感兴趣 脱机I/O方式和假脱机系统 一、脱机I/O方式二、假脱机系统1、假脱机技术(SPOOLing, simulataneaus …

一日难再晨及时当勉励 date

文章目录 Linux shell 获取更改系统时间默认输入显示时区世界协调时格式化日期更多信息 Linux shell 获取更改系统时间 … note:: 时光只解催人老,不信多情,长恨离亭,泪滴春衫酒易醒。 - 晏殊《采桑子时光只解催人老》date命令可以用来打印…

GPT 商店强势来袭,人人都要有自己的 GPTs

作者:苍何,前大厂高级 Java 工程师,阿里云专家博主,CSDN 2023 年 实力新星,土木转码,现任部门技术 leader,专注于互联网技术分享,职场经验分享。 🔥热门文章推荐&#xf…