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

什么是事件驱动

  • 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());
    }
}

测试

在这里插入图片描述

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

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

相关文章

AI安全综述

1、引言 AI安全这个话题&#xff0c;通常会引伸出来图像识别领域的对抗样本攻击。下面这张把“熊猫”变“猴子”的攻击样例应该都不陌生&#xff0c;包括很多照片/视频过人脸的演示也很多。 对抗样本的研究领域已经具备了一定的成熟性&#xff0c;有一系列的理论来论述对抗样本…

【JavaEE初阶二】 Thread类及常见方法

1. 线程的创建 主要来简单学习一下以下几种方法&#xff1a; 1.1 继承 Thread 类 具体代码见前面的一章节&#xff0c;主体的步骤有以下几部分&#xff1b; 1、继承 Thread 来创建一个自定义线程类MyThread class MyThread2 extends Thread{//重写run方法Overridepublic void …

VScode远程连接服务器,Pycharm专业版下载及远程连接(深度学习远程篇)

Visual Code、PyCharm专业版&#xff0c;本地和远程交互。 远程连接需要用到SSH协议的技术&#xff0c;常用的代码编辑器vscode 和 pycharm都有此类功能。社区版的pycharm是免费的&#xff0c;但是社区版不支持ssh连接服务器&#xff0c;只有专业版才可以&#xff0c;需要破解…

xlua源码分析(四) lua访问C#的值类型

xlua源码分析&#xff08;四&#xff09; lua访问C#的值类型 上一节我们主要探讨了C#是如何使用interface和delegate访问lua层的table和function的&#xff0c;本节我们跟着Examples 05_NoGc&#xff0c;来看看xlua是如何实现lua层无gc访问C#的值类型的。 首先例子中用到的lua…

在线快速获取UDID教程

第一步 进入UDID 登录咕噜分发官网(https://www.gulufenfa.com) 点击工具箱 进入控制台—【开发者工具】—【UDID】 第二步 获取UDID 进入UDID快速获取页面 因需要历史UDID所以需要登录您在咕噜分发的账号 用苹果手机扫描二维码根据操作UDID 第三步 手机操作教程 手机…

科技云报道:开源才是大模型的未来?

科技云报道原创。 一年前&#xff0c;ChatGPT横空出世&#xff1b;7个多月后&#xff0c;Meta宣布开源LLaMA 2&#xff0c;并且可免费商用。 这一天&#xff0c;也成为大模型发展的分水岭。短时间内&#xff0c;LLaMA 2对一些闭源的大模型厂商造成了致命性的打击。 随后&…

CGAL的三角曲面网格骨架化

1、介绍 马模型的曲线骨架。 骨架是用于分割、形状匹配、重建、虚拟导航等的有效形状抽象。顾名思义&#xff0c;曲线骨架是曲线结构的图&#xff08;1D&#xff09;。对于3D几何体来说&#xff0c;它不是由表面&#xff08;2D&#xff09;组成的中轴。如图所示&#xff0c;形…

VMvare虚拟机之文件夹共享防火墙设置

目录 一.jdk的配置&TomCat的配置 1.1 jdk配置 1.2 tomcat配置 二.文件夹共享功能 2.1 作用 2.2.高级共享和普通共享 三.防火墙设置 3.1 入站规则和出站规则 四.附图-思维导图 一.jdk的配置&TomCat的配置 建立一个共享文件夹&#xff0c;将jdk文件和tomcat文…

嵌入式——RTC内置实时时钟

学习目标 理解原理图RTC设计部分掌握初始化RTC掌握设置时间掌握读取时间学习内容 RTC原理图 RTC结构框图 RTC时钟 开发流程 加载依赖。gd32f4xx_rtc.c,gd32f4xx_pmu.c初始化RTC。时钟配置。获取时钟。RTC初始化 // 电池管理加载 rcu_periph_clock_enable(RCU_PMU); pmu_back…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Progress进度条组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Progress进度条组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Progress组件 进度条也是UI开发最常用的组件之一&#xff0c;进度条组件…

分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】

分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】 目录 分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCSO-…

docker部署kafka zookeeper模式集群

单机模式链接&#xff1a;https://blog.csdn.net/wsdhla/article/details/133032238 kraft集群模式链接&#xff1a;部署Kafka_kafka 部署-CSDN博客 zookeeper选举机制举例&#xff1a; 目前有5台服务器&#xff0c;每台服务器均没有数据&#xff0c;它们的编号分别是1,2,3,4,5…

如何通过内网穿透实现远程访问本地Linux SVN服务

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

Nature | 大型语言模型(LLM)能够发现和产生新知识吗?

大型语言模型&#xff08;LLM&#xff09;是基于大量数据进行预训练的超大型深度学习模型。底层转换器是一组神经网络&#xff0c;这些神经网络由具有自注意力功能的编码器和解码器组成。编码器和解码器从一系列文本中提取含义&#xff0c;并理解其中的单词和短语之间的关系。通…

python 通过opencv及face_recognition识别人脸

效果&#xff1a; 使用Python的cv2库和face_recognition库来进行人脸检测和比对的 0是代表一样 认为是同一人。 代码&#xff1a; pip install opencv-python pip install face_recognition# 导入cv2库&#xff0c;用于图像处理 import cv2 # 导入face_recognition库&#…

跨域 - CORS跨域资源共享介绍

目录 1&#xff0c;介绍2&#xff0c;简单请求判定交互规范 3&#xff0c;非简单请求交互规范1&#xff0c;发送预检请求2&#xff0c;预检请求响应3&#xff0c;浏览器发送真实的请求&#xff0c;服务器完成真实的响应。 附带身份凭证 相关内容&#xff1a; 浏览器同源策略和跨…

C# WPF上位机开发(MySql访问)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了数据库sqlite&#xff0c;不过这是一种小型的数据库&#xff0c;作为内部使用还可以。但是&#xff0c;如果要与外面的其他供应商进…

doris数据模型,06-Aggregate(聚合模型)

聚合模型的特点 将表中的列分为Key和Value。 Key是数据的维度列&#xff0c;比如时间&#xff0c;地区等等。key相同时会发生聚合。 Value是数据的指标列&#xff0c;比如点击量&#xff0c;花费等等。 每个指标列还会有自己的聚合函数&#xff0c;如&#xff1a;sum&#xff…

路由器常见故障分析及处理方法!

对当前的大多数网络来说&#xff0c;无论是实现网络互连还是访问Internet&#xff0c;路由器是不可或缺的。 由于路由器的重要性&#xff0c;对它的管理就成了维护人员的日常工作中重要的一部分&#xff0c;而路由器的故障分析和排除也是令许多维护人员极为困扰的问题之一。 路…

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

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