Spring Boot框架强大的事件驱动模型(ApplicationEvent)

文章目录

    • 前言
    • 应用场景
      • 异步处理
      • 事务边界外的操作
      • 跨微服务通信
      • 系统监控与日志聚合
      • UI更新
      • 生命周期管理
      • 工作流或业务流程
      • 缓存同步
    • 小试牛刀
      • 定义事件
      • 实现事件处理器
      • 注册事件处理器
      • 发布事件
      • 测试事件
    • 写在最后

前言

在Spring Boot应用中,事件处理器是指那些处理特定类型事件的对象。SpringBoot框架提供了强大的事件驱动模型(ApplicationEvent),允许应用程序组件之间通过发布和监听事件来进行松耦合的通信。在实际的生产场景中,我们可以使用事件进行缓存、邮件、日志解耦,从而提高系统性能。

应用场景

异步处理

当某个业务操作完成后,需要触发一系列耗时较长的后台任务(如发送邮件通知、更新缓存、清理资源、日志记录等),但又不想阻塞主线程或直接影响用户响应时间。此时,可以通过发布一个事件,由专门的异步事件处理器监听并执行这些任务。

事务边界外的操作

当某个业务操作在一个数据库事务内完成,而后续操作(如更新搜索索引、消息队列投递)需要在事务提交后进行,以避免数据不一致。可以在事务成功提交后发布一个事件,让监听器在事务外部处理这些后续操作。

跨微服务通信

在微服务体系中,不同的服务通常独立部署。当一个服务发生重要状态变更(如订单创建、用户注册、库存更新等)时,可以通过事件机制向其他服务广播事件,使它们能及时响应并更新自身状态,无需直接调用API。这符合“发布-订阅”模式,有助于降低服务间的耦合度。

系统监控与日志聚合

系统中的关键操作或异常情况可以触发特定事件,如性能指标收集、审计日志记录、报警触发等。事件处理器可以统一收集这些事件,将其发送到监控系统、日志平台或报警服务,实现系统的集中监控和日志分析。

UI更新

在Web应用中,尤其是单页应用(SPA)或使用WebSocket等技术实现实时通信的场景,前端界面需要对后端状态变化做出即时响应。后端可以发布事件通知前端更新特定区域的内容,如刷新用户通知列表、显示新消息提示等。

生命周期管理

Spring框架内部及用户自定义组件的生命周期事件也是Event Handler的典型应用场景。例如,Spring容器启动和关闭时会发布相关事件,允许开发者注册监听器来执行初始化配置、资源清理等工作。此外,对于自定义的领域对象,如用户账户的激活、注销等状态变更,也可以通过事件进行通知和处理。

工作流或业务流程

在复杂的业务流程中,如订单审批、文章审核等,每个步骤的完成可以触发事件,促使流程推进到下一个阶段。事件处理器负责检查条件、更新状态,并可能触发新的事件,从而驱动整个流程自动运行。

缓存同步

当主数据发生更改时,如数据库记录的增删改,可以通过事件通知缓存系统更新或失效相应的缓存项,确保数据一致性。
总结来说,Spring Event Handler适用于任何需要在不同组件、服务或层级之间解耦通信、异步处理任务或响应特定状态变更的场景,它有助于构建松耦合、响应式和灵活扩展的系统架构。

小试牛刀

以下是一个Spring Boot事件处理器的示例,我们可以在事件处理其中实现自己想要的逻辑代码。

定义事件

首先,定义一个自定义事件类,它通常继承自ApplicationEvent。例如,假设我们要创建一个表示用户注册成功的事件:

import org.springframework.context.ApplicationEvent;

/**
 * UserRegisteredEvent
 * @author senfel
 * @version 1.0
 * @date 2024/4/25 16:38
 */
public class UserRegisteredEvent extends ApplicationEvent {
    private final String username;
    public UserRegisteredEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

这里,UserRegisteredEvent包含了注册成功的用户名信息。

实现事件处理器

创建一个实现了ApplicationListener接口的类,指定它监听的事件类型。在本例中,我们将创建一个监听UserRegisteredEvent的处理器:

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * UserRegisteredEventHandler
 * @author senfel
 * @version 1.0
 * @date 2024/4/25 16:39
 */
@Component
public class UserRegisteredEventHandler implements ApplicationListener<UserRegisteredEvent> {
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        String username = event.getUsername();
        System.out.println("User registered: " + username);

        // TODO 具体的业务,这里直接打印完成的信息
        System.out.println("business action complete: " + username);
    }
}

onApplicationEvent方法会在接收到匹配类型的事件时被调用。在这个示例中,我们只是简单地打印出已注册用户的用户名,并演示打印缓存结果。

注册事件处理器

由于UserRegisteredEventHandler是一个Spring Bean,只需将其纳入Spring容器管理,它就会自动成为事件监听器。通常,您可以通过@Component注解将其声明为一个Spring Bean,并确保该类所在的包(或指定的包)在Spring Boot主类的@SpringBootApplication注解扫描范围内。

import org.springframework.stereotype.Component;

@Component
public class UserRegisteredEventHandler implements ApplicationListener<UserRegisteredEvent> {
   // ...
}

或者,如果您使用Java配置类,可以显式注册该监听器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
   @Bean
   public UserRegisteredEventHandler userRegisteredEventHandler() {
       return new UserRegisteredEventHandler();
   }
}

发布事件

当用户注册成功时,在相应的业务逻辑处发布UserRegisteredEvent。通常,您可以在服务层、控制器或其他适当的地方发布事件:

import com.example.ccedemo.event.UserRegisteredEvent;
import com.example.ccedemo.service.UserService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

/**
 * UserServiceImpl
 * @author senfel
 * @version 1.0
 * @date 2024/4/25 16:45
 */
@Service
public class UserServiceImpl implements UserService, ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    @Override
    public void getUsername(String username) {
        // 执行注册逻辑...
        System.out.println("User registered successfully: " + username);
        // 发布UserRegisteredEvent
        publisher.publishEvent(new UserRegisteredEvent(this, username));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
}

上述代码中,UserService实现了ApplicationEventPublisherAware接口,Spring会自动注入ApplicationEventPublisher实例。在registerUser方法中,完成用户注册逻辑后,发布UserRegisteredEvent。此时,UserRegisteredEventHandler会接收到该事件并执行相应的处理逻辑。

测试事件

我们直接新增一个测试类调用获取用户信息方法:

import com.example.ccedemo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;

/**
 * SpringEventTest
 * @author senfel
 * @version 1.0
 * @date 2024/4/25 16:49
 */
@SpringBootTest
public class SpringBootEventTest {
    @Resource
    private UserService userService;
    @Test
    public void test(){
        userService.getUsername("senfel");
    }
}

在这里插入图片描述

写在最后

总结起来,要创建一个Spring Boot事件处理器,需要:

1、定义一个继承自ApplicationEvent的自定义事件类;
2、实现一个ApplicationListener,指定监听的事件类型,并在onApplicationEvent方法中编写处理逻辑;
3、将事件处理器注册为Spring Bean,使其能自动监听发布的事件;
4、在适当的位置发布事件,通常使用ApplicationEventPublisher。

实际应用中,我们可以根据业务需求定义更多类型的事件和对应的处理器,利用事件驱动模型来解耦组件间的交互。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实时采集麦克风并播放(springboot+webscoekt+webrtc)

项目技术 springbootwebscoektwebrtc 项目介绍 项目通过前端webrtc采集麦克风声音&#xff0c;通过websocket发送后台&#xff0c;然后处理成g711-alaw字节数据发生给广播UDP并播放。 后台处理项目使用线程池(5个线程)接受webrtc数据并处理g711-alaw字节数组放到Map容器中&…

将针孔模型相机 应用到3DGS

Motivation 3DGS 的 投影采用的是 CG系的投影矩阵 P P P, 默认相机的 principal point (相机光心) 位于图像的中点处。但是 实际应用的 绝大多数的 相机 并不满足这样一个设定&#xff0c; 因此我们 需要根据 f , c x , c y {f,c_x, c_y} f,cx​,cy​ 这几个参数重新构建3D …

Linux 安装 nvm,并使用 Jenkins 打包前端

文章目录 nvm是什么nvm下载nvm安装设置 nvm 环境变量设置 Jenkins 打包命令 nvm是什么 nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装…

电脑提示msvcp100.dll丢失的解决方法,多种有效的解决方法分享

在日常使用电脑进行工作的时候&#xff0c;我们常常依赖于各种高效软件来辅助完成任务&#xff0c;提升工作效率。然而&#xff0c;当你满怀期待地双击启动某个至关重要的办公软件时&#xff0c;屏幕上却弹出了一个令人措手不及的错误提示&#xff1a;“msvcp100.dll文件丢失”…

二. 搭建Nginx 直播流程服务器

目录 1. 前言 2. 安装 Nginx 依赖 3.下载源码 4. 编译安装 5.配置 rtmp 服务 6.验证配置 1. 前言 服务器由 NGINXRTMP 构成。 NGINX 是 HTTP 服务器&#xff0c; RTMP 是附加模块。 其中 NGINX 我选择的是用 源码编译方式 进行安装&#xff0c;因为这种方式可以自定义…

基于python语言气象水文数据处理及精美科研绘图实践技术

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;Python能够运行在Linux、Windows、Macintosh、AIX操作系统上及不同平台&#xff08;x86和arm&#xff09;&#xff0c;Python简洁的语法和对动态输入的支持&#xff0c;再加上解释性语言的本质&…

C语言中的三大循环

C语言中为我们提供了三种循环语句&#xff0c;今天我就来与诸君细谈其中之奥妙。循环这一板块总结的内容较多&#xff0c;而且&#xff0c;很重要&#xff01;&#xff08;敲黑板&#xff01;&#xff01;&#xff01;)&#xff0c;所以诸君一定要对此上心&#xff0c;耐住性子…

修复所有 bug 并不能解决所有问题

原文&#xff1a;jeffpsherman - 2024.04.08 在软件领域&#xff0c;如同在制造业&#xff0c;有些问题是由于 bug 或“特殊原因”引发的&#xff0c;而有些则是“常见原因”&#xff0c;这是由于系统设计和实现的性质所导致的。修复 bug 就是移除特殊原因&#xff0c;消除 bu…

go语言实现简单认证样例

目录 1、代码实现样例 2、postman调用 1、代码实现样例 package mainimport ("net/http""strings""github.com/dgrijalva/jwt-go""github.com/gin-gonic/gin" )var (// 密钥&#xff0c;用于验证 JWT 令牌signingKey []byte("…

上班太闲了,一坐就是一天,有没有什么副业可以干的?

一、别做兼职&#xff0c;做副业 兼职&#xff0c;仅仅是用时间换取报酬&#xff0c;短暂且有限&#xff0c;实质上仍是雇佣劳动。副业则不同&#xff0c;它依托你的独特价值换取长久回报&#xff0c;犹如你的第二事业。 或许你还不太清楚兼职的局限性&#xff0c;以下是一些…

上位机开发PyQt5(一)【创建窗口、窗口标题、气泡、显示图片和图标、显示文字】

目录 一、 第一个Qt窗口 二、PyQt模块简介 三、窗口标题和气泡 setWindowTitle resize setToolTip 四、标签QLabel显示图片和图标 setPixmap setWindowIcon resize(label.pixmap().size()) 五、标签QLabel显示文字 setText QFont setPointSize setFont set…

ios 打印选择纸张

问题描述&#xff1a; 手机App开发中的打印功能&#xff0c;在android中可以选择打印的纸张是A4 A5 等&#xff0c;但是在ios系统中不能选择纸张&#xff0c;一般情况下会只有一个纸类型。 原因解释&#xff1a; 因为在打印机的配置页中可以设置打印机的当前纸张大小&#xff…

sql今天学习总结

排序order by&#xff08;默认升序&#xff09; order by id desc(降序排序&#xff09; order by id,number&#xff08;先按id排再按name排序&#xff09; in,not in and or 通配符 where name like "Aa%";选取所有以Aa开头的名字 like "%r" 以r结…

从关键新闻和最新技术看AI行业发展(2024.2.12-2.25第十七期) |【WeThinkIn老实人报】

写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术&#xff0c;同时Rocky会对这些关键信息进行解读&#xff0c;力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&#x1f4aa; 欢迎大家关注Rocky的公众号&…

C语言-atoi和atof函数的使用

人生应该树立目标&#xff0c;否则你的精力会白白浪费。&#x1f493;&#x1f493;&#x1f493; 目录 •&#x1f319;知识回顾 &#x1f34b;知识点一&#xff1a;atoi函数的使用和实现 • &#x1f330;1.函数介绍 • &#x1f330;2.代码演示 • &#x1f330;3.atoi函数的…

LeetCode78:子集

题目描述 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的 子集 &#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 代码 class Solution { public:vector<vector<int>> res;vector<…

DAM与开源AI名片B2B2C商城系统深度融合,引领企业数字化升级

在数字化浪潮的推动下&#xff0c;内容科技日益成为企业营销战略中的核心力量。DAM&#xff08;数字资产管理&#xff09;以其卓越的内容管理能力&#xff0c;引领企业进入数字资产高效管理与利用的新时代。与此同时&#xff0c;开源AI名片B2B2C商城系统也以其强大的功能和创新…

【Linux】进程的控制①之进程创建与进程退出

一 、进程的创建 1、fork函数 fork函数功能&#xff1a;从已经存在的进程中创建一个新进程。新进程为子进程&#xff0c;原进程为父进程。 fork函数创建进程过后&#xff0c;父子进程代码和数据是共享的。在前面也讲过。 2.函数的返回值 如果进程创建成功&#xff0c;给父进…

PCB设计:差分线

1、差分线的定义 差分线是两条平行的、等长的走线,传输相位差180度的同一个信号,是一根传输“+信号”,一根传输“-信号”。两个信号相减得到2倍强度的有用信号。而两根信号线上的干扰信号相减之后也就没了。 2、差分线的优势 差分信号和普通的单端信号走线相比,最明显的优…

ftp: connect :连接被拒绝(win10)

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用命令测试显示&#xff1a;“FTP拒绝连接” 二、原因分析 第一时间查看ftp服务启动了没哟&#xff0c;相关服务有2个&#xff0c;一个是【Microsoft FTP Service】一个是【IIS 管理服务】&#xf…
最新文章