SpringBoot Event,事件驱动轻松实现业务解耦

news2025/2/9 2:16:49

什么是事件驱动

  • Spring 官方文档
  • AWS Event Driven

简单来说事件驱动是一种行为型设计模式,通过建立一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都能自动接收通知并更新。即将自身耦合的行为进行拆分,使拆分出的行为根据特定的状态变化(触发条件)自动触发。

事件驱动核心组件

  1. 被观察者(Subject): 负责维护观察者列表,并在状态变化时通知观察者。被观察者可以是一个类或对象。
  2. 观察者(Observer): 定义一个更新接口,使得在状态变化时能够接收被观察者的通知。观察者对象需要注册到被观察者上,以便接收通知。
  3. 通知(Notify): 被观察者在状态变化时会调用观察者的更新方法,通知它们有关状态的变化。
  4. 订阅(Subscribe)和取消订阅(Unsubscribe): 观察者可以通过订阅和取消订阅操作来注册和注销对被观察者的关注。

实现方式

  • Guava
  • Spring Event

版本依赖

  • JDK 17
  • Spring Boot 3.2.0
  • Guava 33.0.0-jre

image-20231226033126185

Tips

在仅将事件拆分出事件对象与事件监听对象后,通过事件总线推送事件,仍是单线程执行,在SpringBoot中需要两个注解来实现异步执行。

  • @EnableAsync 类注解,在配置类上标记开启SpringBoot异步线程
  • @Async 方法注解,表示注解的方法异步执行。@Async 注解的方法仅在通过容器中获取的对象调用方法为异步执行,非容器对象方法调用无效

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>33.0.0-jre</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

基于Guava实现

创建事件对象

import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

/**
 * Guava 实现事件对象
 */
@Data
public class GuavaEvent implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private String data;

    public GuavaEvent(String data) {
        this.data = data;
    }
}

创建事件监听

import com.google.common.eventbus.Subscribe;
import com.yiyan.study.guava.event.GuavaEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * Guava 事件监听
 */
@Component
@Slf4j
public class GuavaEventListener {

    @Subscribe
    @Async
    public void onEvent(GuavaEvent event) throws InterruptedException {
        // 模拟业务耗时
        Thread.sleep(500);
        log.info("Guava - GuavaEvent onEvent : {}", event.getData());
    }
}

创建事件总线,并注册事件监听

import com.google.common.eventbus.EventBus;
import com.yiyan.study.guava.listener.GuavaEventListener;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Guava 事件总线
 */
@Configuration
public class GuavaEventBus {

    @Resource
    private GuavaEventListener guavaEventListener;

    @Bean
    public EventBus initialize() {
        EventBus eventBus = new EventBus();
        // 注册监听
        eventBus.register(guavaEventListener);
        return eventBus;
    }
}

编写Controller测试接口

import com.google.common.eventbus.EventBus;
import com.yiyan.study.guava.event.GuavaEvent;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 事件测试Controller
 */
@Slf4j
@RestController
public class EventController {

    @Resource
    private EventBus eventBus;

    @GetMapping("/event/guava")
    public void guavaPost(@RequestParam(value = "message") String message) {
        StopWatch stopWatch = new StopWatch("guava-event");
        stopWatch.start();
        eventBus.post(new GuavaEvent(message));
        stopWatch.stop();
        log.info("guava-event Request Log: \r{}", stopWatch.prettyPrint());
    }
}

基于Spring Event实现

Spring Boot自己维护了一个ApplicationEventPublisher的事件注册Bean,通过@EventListener注解标识监听事件。无需自己在注册一个消息总线。

创建事件对象

import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

/**
 * Spring 事件对象
 */
@Data
public class SpringEvent implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private String data;

    public SpringEvent(String data) {
        this.data = data;
    }
}

注册监听对象

import com.yiyan.study.spring.event.SpringEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * Spring event listener
 */
@Component
@Slf4j
public class SpringEventListener {
    
    @EventListener
    @Async
    public void onApplicationEvent(SpringEvent event) throws InterruptedException {
        // 模拟业务耗时
        Thread.sleep(500);
        log.info("SpringEvent received: {}", event.getData());
    }
}

补充测试接口

package com.yiyan.study.controller;

import com.google.common.eventbus.EventBus;
import com.yiyan.study.guava.event.GuavaEvent;
import com.yiyan.study.spring.event.SpringEvent;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 事件测试Controller
 */
@Slf4j
@RestController
public class EventController {

    @Resource
    private EventBus eventBus;
    @Resource
    private ApplicationEventPublisher eventPublisher;

    @GetMapping("/event/guava")
    public void guavaPost(@RequestParam(value = "message") String message) {
        StopWatch stopWatch = new StopWatch("guava-event");
        stopWatch.start();
        eventBus.post(new GuavaEvent(message));
        stopWatch.stop();
        log.info("guava-event Request Log: \r{}", stopWatch.prettyPrint());
    }

    @GetMapping("/event/spring")
    public void springPublish(@RequestParam(value = "message") String message) {
        StopWatch stopWatch = new StopWatch("spring-event");
        stopWatch.start();
        eventPublisher.publishEvent(new SpringEvent(message));
        stopWatch.stop();
        log.info("spring-event Request end: \r{}", stopWatch.prettyPrint());
    }
}

测试

在这里插入图片描述

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

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

相关文章

vscode括号颜色突然变成白色的了,怎么解决

更新版本后发现vscode的各种括号都变成了白色&#xff0c;由于分色括号已经使用习惯&#xff0c;突然变成白色非常不舒服&#xff0c;尝试多次后&#xff0c;为大家提供一下几种解决方式&#xff0c;希望能帮到同样受到此种困惑的你&#xff1a; 第一种&#xff1a; 首先打开…

TYN-02A-Ⅱ 太阳能警示灯

应用范围: 可安装在电线杆&#xff0c;路灯&#xff0c;围挡&#xff0c;交 通护栏及各种杆式固体等场所起警示作用。 产品特点&#xff1a; 采用进口PS材质; 光控无开关&#xff0c;白天不闪&#xff0c;昏暗环境自动闪烁&#xff0c;无需手动操作&#xff0c;省时省事; …

blender使用faceit绑定自己的表情动作

blender使用faceit绑定自己的表情控制模型 faceit是个神器&#xff0c;来记录一下如何让表情动起来保持相对位置头部分离&#xff0c;方便后续绑定faceitfaceit的注册rig生成地标Animate可以修正表情烘培之前记得保存使用Faceit的整个流程 faceit是个神器&#xff0c;来记录一下…

test-03-java 单元测试框架 testNG 入门介绍 junit/junit5/testNG 详细对比

拓展阅读 test-01-java 单元测试框架 junit 入门介绍 test-02-java 单元测试框架 junit5 入门介绍 test-03-java 单元测试框架 testNG 入门介绍 junit/junit5/testNG 详细对比 test assert-01-Google Truth 断言 test 系统学习-03-TestNG Spock testng 入门使用教程 开源…

Oracle 学习(2)

过滤和排序数据 where条件过滤 日期格式 查询10号部门的员工信息&#xff1a;SQL> select * from emp where deptno10 查询”KING”的信息&#xff1a;SQL> select * from emp where ename KiNg 未选定行。 注意&#xff1a;字符串大小写敏感。 SQL> selec…

【论文笔记】3D Gaussian Splatting for Real-Time Radiance Field Rendering

原文链接&#xff1a;https://arxiv.org/abs/2308.04079 1. 引言 网孔和点是最常见的3D场景表达&#xff0c;因其是显式的且适合基于GPU/CUDA的快速栅格化。神经辐射场&#xff08;NeRF&#xff09;则建立连续的场景表达便于优化&#xff0c;但渲染时的随机采样耗时且引入噪声…

时序预测 | Matlab实现SSA-CNN-BiLSTM麻雀算法优化卷积双向长短期记忆神经网络时间序列预测

时序预测 | Matlab实现SSA-CNN-BiLSTM麻雀算法优化卷积双向长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab实现SSA-CNN-BiLSTM麻雀算法优化卷积双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SSA-CNN-BiLSTM麻雀算…

关于Python里xlwings库对Excel表格的操作(十八)

这篇小笔记主要记录如何【设置单元格数据的对齐方式】。前面的小笔记已整理成目录&#xff0c;可点链接去目录寻找所需更方便。 【目录部分内容如下】【点击此处可进入目录】 &#xff08;1&#xff09;如何安装导入xlwings库&#xff1b; &#xff08;2&#xff09;如何在Wps下…

Vue2和Vue3组件间通信方式汇总(2)------$emit

组件间通信方式是前端必不可少的知识点&#xff0c;前端开发经常会遇到组件间通信的情况&#xff0c;而且也是前端开发面试常问的知识点之一。接下来开始组件间通信方式第二弹------$emit,并讲讲分别在Vue2、Vue3中的表现。 Vue2Vue3组件间通信方式汇总&#xff08;1&#xff0…

7_js_dom编程入门1

Objective&#xff08;本课目标&#xff09; 掌握获取页面元素的常用方法 掌握事件触发案例 能够区分innerText和innerHTML的区别 综合案例训练 1 DOM 介绍 1.1 什么是DOM 文档对象模型&#xff08;Document Object Model&#xff0c;简称DOM&#xff09;&#xff0c;是 …

Grafana二进制部署并配置prometheus数据源

1、获取grafna二进制安装包 https://grafana.com/grafana/download?pggraf&plcmtdeploy-box-1 grafana官网下载地址 [rootambari-hadoop1 ~]# cd /opt/module/grafana/ [rootambari-hadoop1 grafana]# pwd /opt/module/grafana2、在安装自己的安装目录执行 wget https:…

谷歌 SGE 和生成式 AI 在搜索中:2024 年会发生什么

2024 年&#xff0c;Google 的搜索生成体验将如何影响营销&#xff1f;探索 AI 驱动的搜索趋势、SERP 中的 SGE、自然流量影响等。 最初&#xff0c;Labs 中的 Google 搜索生成体验 &#xff08;SGE&#xff09; 实验预计将于 2023 年 12 月“结束”。但随着谷歌实验室网站的最…

1096. 地牢大师(蓝桥杯/bfs宽搜求最小距离)

题目&#xff1a; 1096. 地牢大师 - AcWing题库 输入样例&#xff1a; 3 4 5 S.... .###. .##.. ###.###### ##### ##.## ##...##### ##### #.### ####E1 3 3 S## #E# ###0 0 0输出样例&#xff1a; Escaped in 11 minute(s). Trapped! 思路&#xff1a;bfs&#xff08;三维…

2007年AMC8数学竞赛中英文真题典型考题、考点分析和答案解析

今天&#xff0c;我们来继续研究AMC8竞赛的真题。通过反复研究历年真题&#xff0c;不仅可以掌握AMC8这个竞赛的命题规律和常见考点&#xff0c;通过真题的详细解析可以建立自己的解题思路、举一反三&#xff0c;还可以通过做真题不断发现自己的薄弱点查漏补缺。 今天我们来看看…

【单调队列】LeetCode1499:满足不等式的最大值

涉及知识点 单调队列 题目 给你一个数组 points 和一个整数 k 。数组中每个元素都表示二维平面上的点的坐标&#xff0c;并按照横坐标 x 的值从小到大排序。也就是说 points[i] [xi, yi] &#xff0c;并且在 1 < i < j < points.length 的前提下&#xff0c; xi &…

蓝牙物联网在智慧医疗中的应用

物联网技术开启了万物互联的时代&#xff0c;并且随着智慧城市建设的加速推进及物联网技术对各行业的逐步渗透&#xff0c;“智慧”概念应运而生&#xff0c;诸如智慧能源、智慧交通、智慧医疗等“遍地开花”&#xff0c;可以说&#xff0c;物联网技术给各行业带来了产业模式上…

时间Date

你有没有思考过时间问题&#xff1a; 前端为什么可以直接看见时间格式的数据 后端怎么接受的数据&#xff0c;怎么处理的 一般来说&#xff1a;前端传输来数据都是时间格式的字符串&#xff0c;那么后端需要能够解析时间格式的字符串&#xff0c;归功于JSONFormat ,可以解析…

<meta name=“Keywords“ content=““ >、<meta name=“Description“ content=““ > 等用法解释

今天在看网站代码&#xff0c;发现类似<meta name"Keywords" content"" >、<meta name"Description" content"" >这样的写法&#xff0c;不知道具体代表什么意思&#xff0c;于是上网搜了一下&#xff0c;下面是在网上找到…

CEC2013(python):六种算法(RFO、PSO、CSO、WOA、DBO、ABC)求解CEC2013

一、六种算法简介 1、红狐优化算法RFO 2、粒子群优化算法PSO 3、鸡群优化算法CSO 4、鲸鱼优化算法WOA 5、蜣螂优化算法DBO 6、人工蜂群算法 &#xff08;Artificial Bee Colony Algorithm, ABC&#xff09; 二、6种算法求解CEC2013 &#xff08;1&#xff09;CEC2013简…

Ai企业系统源码 Ai企联系统源码 商用去授权 支持文心 星火 GPT4等等20多种接口

智思Ai系统2.4.9版本去授权&#xff08;可商用&#xff09;支持市面上所有版本的接口例如&#xff1a;文心、星火、GPT4等等20多种接口&#xff01;代过审AI小程序类目&#xff01;&#xff01;&#xff01; 安装步骤&#xff1a; 1、在宝塔新建个站点&#xff0c;php版本使用…