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 {@Serialprivate 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@Asyncpublic 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 {@Resourceprivate GuavaEventListener guavaEventListener;@Beanpublic 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 {@Resourceprivate 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 {@Serialprivate 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@Asyncpublic 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 {@Resourceprivate EventBus eventBus;@Resourceprivate 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.hqwc.cn/news/300368.html

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

相关文章

Rancher小白学习之路

官网&#xff1a;http://docs.rancher.cn/docs/rancher1/rancher-service/load-balancer/_indexhttp://docs.rancher.cn/docs/rancher1/rancher-service/load-balancer/_indexRancher2.5集群搭建&K3S生产环境搭建手册 - 知乎 【rancher教程】十年运维大佬两小时带你搞定ran…

Django-REST-Framework 如何快速生成Swagger, ReDoc格式的 REST API 文档

1、API 接口文档的几种规范格式 前后端分离项目中&#xff0c;使用规范、便捷的API接口文档工具&#xff0c;可以有效提高团队工作效率。 标准化的API文档的益处&#xff1a; 允许开发人员以交互式的方式查看、测试API接口&#xff0c;以方便使用将所有可暴露的API接口进行分…

C语言字符串处理提取时间(ffmpeg返回的时间字符串)

【1】需求 需求&#xff1a;有一个 “00:01:33.90” 这样格式的时间字符串&#xff0c;需要将这个字符串的时间值提取打印出来&#xff08;提取时、分、秒、毫秒&#xff09;。 这个时间字符串从哪里来的&#xff1f; 是ffmpeg返回的时间&#xff0c;也就是视频的总时间。 下…

CMMI-项目总体计划模版

目录 1、总体目录结构 2、重点章节概要示例 2.1 第四章 项目管理 2.2 第六章 实施与交付计划 2.3 第七章 运维计划 1、总体目录结构 2、重点章节概要示例 2.1 第四章 项目管理 2.2 第六章 实施与交付计划 2.3 第七章运维计划

Mybatis如何兼容各类日志?

文章目录 适配器模式日志模块代理模式1、静态代理模式2、JDK动态代理 JDBC Logger总结 Apache Commons Logging、Log4j、Log4j2、java.util.logging 等是 Java 开发中常用的几款日志框架&#xff0c;这些日志框架来源于不同的开源组织&#xff0c;给用户暴露的接口也有很多不同…

网络安全保障领域

计算机与信息系统安全---最主要领域 云计算安全 IaaS、PasS、SaaS(裸机&#xff0c;装好软件的电脑&#xff0c;装好应用的电脑) 存在风险&#xff1a;开源工具、优先访问权、管理权限、数据处、数据隔离、数据恢复、调查支持、长期发展风险 云计算安全关键技术&#xff1a;可信…

(C++)DS哈希查找—二次探测再散列(附思路和详细注释)

Description 定义哈希函数为H(key) key%11。输入表长&#xff08;大于、等于11&#xff09;&#xff0c;输入关键字集合&#xff0c;用二次探测再散列构建哈希表&#xff0c;并查找给定关键字。 Input 测试数据组数 1≤&#xfffd;≤50. 每组测试数据格式如下&#xff1a…

【LeetCode刷题笔记】动态规划(四)

背包问题 0-1 背包问题 有一个背包,它的容量为 C现在有 n 种不同的物品,他们的编号分别是 0...n-1。每一种物品只有一个。在这 n 种物品中,第 i 个物品的重量是 w[i],它的价值为 v[i]问题是:可以向这个背包中放哪些物品,使得在不超过背包容量的基础上,背包中物品的总价…

Linux之用户/组 管理

关机&重启命令 shutdown -h now立刻进行关机shutdown -h 11分钟后关机&#xff08;shutdown默认等于shutdown -h 1) -h即halt shutdown -r now现在重新启动计算机 -r即reboot halt关机reboot重新启动计算机sync把内存数据同步到磁盘 再进行shutdown/reboot/halt命令在执行…

List那些坑

很多文章都介绍过这些坑&#xff0c;本文做个记录&#xff0c;并提供解决方案。 1.Arrays.asList的坑 1.1现象 情况1&#xff1a;通过Arrays.asList方法生成的List数据不支持添加操作 使用Arrays.asList方法生成的List数据&#xff0c;不能对其进行删除或者添加操作。代码示例…

申请虚拟VISA卡Pokepay 教程来了

官网地址https://www.pokepay.cc/ ​​​​​ 填写邮箱地址 填写邀请码116780 会有20USD开卡优惠券 限时几天活动

Arduino/Android 蓝牙通信系统设计解决方案

随着当今安全管理的发展需求以及国家对安全监控行业的支持&#xff0c;这几年&#xff0c;安全监控行业发展迅猛&#xff0c;各类监控系统百花齐放。传统的温度监控系统通过有线或其他方式传送温度数据&#xff0c;而本文提出了利用蓝牙无线传输数据的设计方案&#xff0c;这种…