内部应用解耦神器-Spring事件

大家好,我是程序员牛牛,《AI超级个体: ChatGPT与AIGC实战指南》的参与人,10年Java编程程序员。

1. 概述

在做业务开发过程中,有些复杂点的逻辑,可能代码逻辑会很冗长,举一个很简单的例子,如:用户购买产品下单支付,当支付完成后,可能有以下操作:

1710225092784.jpg

如果这些都在一个流程中同步执行下来,不仅代码冗长,耦合度高,而且也不方便维护,此时我们需要做的就是把这三个步骤进行异步解耦,我们第一个想到解决方案的可能是使用消息队列,MQ确实可以解决这个问题,但MQ是比较复杂的,非必要不提升架构复杂度。

如果是微服务架构,涉及到多个服务之间协作,那MQ无疑是最佳选择,但如果是单体架构,完全可以使用更加轻量级的解决方案:Spring Event

2. Spring Event简介

事件是框架中最容易被忽视的功能之一,但也是最有用的功能之一。与 Spring 中的许多其他功能一样,事件发布是ApplicationContext提供的功能之一,它类似发布订阅机制,发布一个事件之后,可以在其他地方监听这个事件,做一些异步处理(当然也支持同步),其实它就是一个观察者模式设计。

3. 使用Spring Event

spring event使用其实很简单:

image.png

下面我们已发送邮件为例,来做一个简单的演示

3.1 其中事件监听的方式有两种

  1. 通过监听器的方式

  2. 通过注解的方式

3.2 通过监听器方式监听事件

3.2.1 定义事件

定义邮件发送事件,需要继承ApplicationEvent, 这里我们列举了两个简单的属性,邮箱地址和邮件内容。

@Getter
@Setter
public class EmailSendEvent extends ApplicationEvent {private String address;private String content;public EmailSendEvent(Object source, String address, String content) {super(source);this.address = address;this.content = content;}
}

3.2.2 定义事件发布者

事件发布类,需要实现ApplicationEventPublisherAware接口, 并且需要把该对象注入到spring容器中

@Component
public class EmailEventPublisher implements ApplicationEventPublisherAware {@Resourceprivate ApplicationEventPublisher publisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher;}/*** 发送邮件** @param address 邮件地址* @param content 邮件内容*/public void sendEmail(String address, String content) {// 发送邮件的逻辑System.out.println("发送邮件:" + address + "," + content);// 发布邮件发送事件publisher.publishEvent(new EmailSendEvent(this, address, content));System.out.println("邮件发送完毕!");}}

3.2.3 创建事件监听类

这里要实现ApplicationListener接口,且同样要把对象注入到Spring容器内

@Component
public class EmailEventListener implements ApplicationListener<EmailSendEvent> {@Overridepublic void onApplicationEvent(EmailSendEvent event) {// 因为此处是同步执行,可以发现,这里收到邮件之后,前面的邮件发送才算完成// 如果需要异步,可以使用注解方式System.out.println("监听器方式,收到邮件:" + event.getAddress() + "," + event.getContent());}}

3.2.4 测试效果

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestEmailDemo {@Autowiredprivate EmailEventPublisher emailEventPublisher;@Testpublic void testSendEmail() {String address = "test@example.com";String content = "Hello, World!";emailEventPublisher.sendEmail(address, content);}
}

此时我们运行该测试类,正常情况下,EmailEventListener类中,将输出收到邮件的信息,我们看看效果

image.png

结果也和我们预想的一致,实际业务中,我们就可以在监听器中,做一些具体的逻辑处理,如把邮件内容发送给具体的用户等等…

3.3 通过注解方式监听事件

在spring4.2版本之后,可以直接使用注解的方式监听事件

事件定义和事件发布,和上面一致,我们增加一个注解监听的方式。

@Component
public class EmailService {/*** 使用注解方式,监听时间* @param sendEvent: email事件*/@EventListener(EmailSendEvent.class)public void receiveEmail(EmailSendEvent sendEvent) {System.out.println("注解监听方式,收到邮件:" + sendEvent.getAddress() + "," + sendEvent.getContent());}}

此时我们再运行上面的测试类,得到的结果:

image.png

可以看到两种监听方式都生效了(ps: 正常使用时,我们只需要选择一种监听方式即可

3.4 同步事件和异步事件

3.4.1 同步事件

默认的spring事件,是同步的,也就是说,事件发送者,需要等到事件被监听完成,才算是一个事件发送完成。

按我们这个例子,是邮件监听完成后,才算邮件事件发送完成,我们来测试一下。

在监听器方式中,加入以下代码:

public void onApplicationEvent(EmailSendEvent event) {// 此处睡眠10秒try {Thread.sleep(1000 * 10);} catch (InterruptedException e) {throw new RuntimeException(e);}// 因为此处是同步执行,可以发现,这里收到邮件之后,前面的邮件发送才算完成// 如果需要异步,可以使用注解方式System.out.println("监听器方式,收到邮件:" + event.getAddress() + "," + event.getContent());}

此时执行测试类,应该是10s后,才算是邮件事件发布完成,看看实际效果:

image.png

对于一些同步场景,我们可以直接使用监听器方式,然后我们更多场景,应该是使用异步事件

3.4.2 异步事件

先使用@EnableAsync注解,开启异步事件支持

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

然后修改EmailService代码,加入@Async注解,来开启异步模式

 @EventListener(EmailSendEvent.class)@Async@Order(1)public void receiveEmail(EmailSendEvent sendEvent) {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("注解监听方式,收到邮件:" + sendEvent.getAddress() + "," + sendEvent.getContent());}

同时我们把监听器方式类,注释掉(只保留异步监听方式)。

此时再执行测试类的时候,应该是邮件事件发送后,不用等待事件监听完成执行,就算是该事件已经发送完成了。

image.png

3.4.3 事件监听顺序

当一个事件,我们有多个监听器时,可以可以使用@Order注解,来指定监听器的顺序,这里@Order(1)来指定这里的顺序为1,可以看看这个注解的源码,默认值为Integer.MAX_VALUE

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {int value() default Integer.MAX_VALUE;
}

我们在EmailService中,增加一个监听器,并指定receiveEmail的顺序为100,receiveEmail2的顺序为1,此时应该先执行receiveEmail2

/*** 使用注解方式,监听时间* @param sendEvent: email事件*/@EventListener(EmailSendEvent.class)@Async@Order(100)public void receiveEmail(EmailSendEvent sendEvent) {
//        try {
//            Thread.sleep(5000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }System.out.println("注解监听方式1,收到邮件:" + sendEvent.getAddress() + "," + sendEvent.getContent());}/*** 使用注解方式,监听时间* @param sendEvent: email事件*/@EventListener(EmailSendEvent.class)@Async@Order(1)public void receiveEmail2(EmailSendEvent sendEvent) {System.out.println("注解监听方式2,收到邮件:" + sendEvent.getAddress() + "," + sendEvent.getContent());}

看看测试结果:

image.png

跟预期一致!

最后我把测试源码都放到码云上了,欢迎关注公众号获取源码!发送消息“Spring Event”即可。

d465119f927be0d100059305e0aa759.jpg

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

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

相关文章

基于Web的论文管理系统设计

目 录 目 录 III 摘 要 V 关键词 V Abstract VI Key Word VI 第一章 绪论 6 1.1系统设计背景 1 1.2系统设计目的与意义 1 1.3国内外现状 2 1.4本文结构 3 第二章 需求分析 3 2.1系统需求分析 4 2.2系统角色设计 4 第三章 系统开发技术 4 3.1 PHP语言简介和特点 5 3.2 Mysql数据…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的铁轨缺陷检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发铁轨缺陷检测系统对于物流行业、制造业具有重要作用。本篇博客详细介绍了如何运用深度学习构建一个铁轨缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不同模…

工业数学模型——冶金烧结配矿(一)

1、工业场景 从很多种铁矿石中选出适合烧结配料的部分铁矿石及其比例&#xff0c;并使其成本最低。 2、数学模型 设Pi代表了第i种原料的成本&#xff0c;xi代表了第i种原料在总配料中的比例&#xff0c;其中i取值为1,2,…,n。计算1吨配料成本&#xff1a; 第种原料的成本是Y…

Caffeine--实现进程缓存

本地进程缓存特点 缓存在日常开发中起着至关重要的作用, 由于存储在内存中, 数据的读取速度非常快,能大量减少对数据库的访问,减少数据库的压力. 缓存分为两类: 分布式缓存, 例如Redis: 优点: 存储容量大, 可靠性更好, 可以在集群间共享缺点: 访问缓存存在网络开销场景: 缓存数…

第二十一天-NumPy

目录 什么是NumPy NumPy使用 1.数组的创建 2.类型转换 3.赠删改查 4.数组运算 5.矩阵运算 什么是NumPy 1.NumPy操作的是多维数组&#xff0c;什么是纬度&#xff1f; NumPy使用 1. 安装 pip install numpy import numpy as np 2.官网&#xff1a; 中文官网&#xff1a…

Python高级二

一、异常 1、定义 异常是在程序执行过程中出现的错误或意外情况。当程序遇到异常时&#xff0c;它会中断当前的执行流程&#xff0c;并尝试找到相应的异常处理机制来解决问题。 2、常见异常类型 SyntaxError&#xff1a;语法错误&#xff0c;通常是代码书写不符合Python语法规则…

LVS集群 ----------------(直接路由 )DR模式部署 (二)

一、LVS集群的三种工作模式 lvs-nat&#xff1a;修改请求报文的目标IP,多目标IP的DNAT lvs-dr&#xff1a;操纵封装新的MAC地址&#xff08;直接路由&#xff09; lvs-tun&#xff1a;隧道模式 lvs-dr 是 LVS集群的 默认工作模式 NAT通过网络地址转换实现的虚拟服务器&…

day01vue学习

day01 一、为什么要学习Vue 1.前端必备技能 2.岗位多&#xff0c;绝大互联网公司都在使用Vue 3.提高开发效率 4.高薪必备技能&#xff08;Vue2Vue3&#xff09; 二、什么是Vue 概念&#xff1a;Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套 **构建用户界面 ** 的 …

网络基础aaa

三次握手 四次挥手 网络模型 TCP or UDP 的特点 如何理解 TCP 的5层协议 TCP的5层协议是指计算机网络体系结构中&#xff0c;与TCP&#xff08;传输控制协议&#xff09;相关的五个层次。这五个层次从高到低依次是&#xff1a;应用层、传输层、网络层、数据链路层和物理层。每…

Flask python开发篇: 写一个简单的接口

第一步&#xff1a;新建flask项目 参考使用pycharm新建一个项目 打开pycharm&#xff0c;根据下面图中箭头顺序&#xff0c;新建一个flask的项目&#xff1b; 第二步&#xff1a;运行项目&#xff0c; 安装成功以后&#xff0c;会有个app.py文件&#xff0c;打开以后&#…

OSI七层模型TCP四层模型横向对比

OSI 理论模型&#xff08;Open Systems Interconnection Model&#xff09;和TCP/IP模型 七层每一层对应英文 应用层&#xff08;Application Layer&#xff09; 表示层&#xff08;Presentation Layer&#xff09; 会话层&#xff08;Session Layer&#xff09; 传输层&#x…

JavaWeb--Maven

一&#xff1a;概述 1.简介 Maven 是专门用于管理和构建 Java 项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 提供了一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布 …… &#xff09; 提供了一套…

【存储】ZYNQ+NVMe小型化全国产存储解决方案

文章目录 1、背景2、基础理论3、设计方案3.1、FPGA设计方案3.1.1、NVMe控制器实现3.1.2、NVMe控制器实现 3.2 驱动软件设计方案3.2.1 读写NVMe磁盘软件驱动3.2.2 NVMe磁盘驱动设计3.2.3 标准EXT4文件系统设计 3.3 上位机控制软件设计方案 4、测试结果4.1 硬件测试平台说明4.2 测…

计算机网络:应用层知识点汇总

文章目录 一、网络应用模型二、域名系统&#xff08;DNS&#xff09;三、文本传输协议&#xff08;FTP&#xff09;四、电子邮件五、万维网和HTTP协议 一、网络应用模型 p2p也就是对等模型 二、域名系统&#xff08;DNS&#xff09; 我们知道&#xff0c;随着人们建立一个网站…

打字通小游戏制作教程:用HTML5和JavaScript提升打字速度

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

Mac M1:通过docker安装RocketMQ、RocketMQ-Dashboard

0. 引言 最近本地启动以前docker安装的rocketmq发现报错了&#xff0c;因为是从老mac迁移过来的&#xff0c;发现支持的芯片还是amd的&#xff0c;于是重新在docker下安装rocketmq&#xff0c;并记录下步骤&#xff0c;方便大家后续参考。 1. 步骤 1、先下载项目源码 git c…

记录一个vue编辑的移动端页面

<template><div class"wrap"><el-form :model"queryParams" ref"queryForm" size"small" :inline"true" label-width"120px"><el-form-item label"班级" prop"classId"…

运维自动化之ansible

pxe 一键安装操作系统 操作系统只是提供一个平台 lnmp 需要多软件协同完成的一个简单项目 服务器正常运行 日常运维 巡检 服务器上的软件正常运行 zabbix 普罗米修斯 系统调优&#xff0c;架构调优 云计算核心职能 搭建平台架构 日常运营保障 性能效率优化 相关工具 代…

【SQL】601. 体育馆的人流量(with as 临时表;id减去row_number()思路)

前述 知识点学习&#xff1a; with as 和临时表的使用12、关于临时表和with as子查询部分 题目描述 leetcode题目&#xff1a;601. 体育馆的人流量 思路 关键&#xff1a;如何确定id是连续的三行或更多行记录 方法一&#xff1a; 多次连表&#xff0c;筛选查询方法二&…

护眼灯什么价位的好用?推荐五款好价护眼台灯

如今&#xff0c;我们不难发现许多年轻人早早地就戴上了眼镜&#xff0c;近视问题日益严重。在改善近视问题的众多因素中&#xff0c;营造适宜的照明环境&#xff0c;特别是选择一款合适的护眼台灯&#xff0c;显得尤为重要。然而&#xff0c;对于初次选购护眼台灯的人来说&…