Spring Application Event 在事件驱动设计中的应用

1. 什么是事件驱动设计

我们先从去餐厅吃饭来辅助理解什么是事件驱动设计,我们从点菜到上菜通常涉及到以下角色:

  • 服务员
  • 厨师
  • 上菜员

不同角色的职责:

  • 服务员负责协助点餐
  • 厨师负责制作菜品
  • 上菜员负责上菜

我们通过事件的角度来考虑整个流程:

  1. 顾客点餐事件,事件内容为菜品与餐位信息;
  2. 厨师收到顾客点餐事件,开始制作菜品;
  3. 厨师完成菜品制作事件,事件内容为菜品与餐位信息;
  4. 上菜员收到菜品制作事件,上菜到指定餐位;

通过以上案例,我们可以将事件驱动设计总结为对业务过程中所发生的事件进行抽象,通过抽象后的事件来考虑代码、应用架构、业务流程编排设计的一种思维。

2. 代码中使用事件发布监听好处

在系统建设过程中通常会遇到两个不同领域耦合的老代码和基于系统规模和时间成本不得不采用妥协方案而导致的耦合,为了应对这种工程实践的现状,考虑可扩展、易维护、可拆分,我们在无法对其进行服务级别隔离时,可以考虑通过事件发布和监听的手段将需要协同处理业务的不同领域代码进行依赖分离。

3. 为什么使用 Spring Application Event

实现事件发布监听的方式多种多样,比较常接触到的有以下几种:

  • 基于 Java 内置观察者模式相关接口实现

  • Guava EventBus

  • Spring Application Event

现在 Java 后端开发主流都是基于 Spring 生态,从减少依赖和提升开发效率方面考虑选择了 Spring Application Event,当然 Guava EventBus 也非常不错,只不过 Guava 的版本管理做的不够好,经常冲突不断,所以我通常在项目里能不用就不用。

4. Spring Application Event 应用案例

4.1. 订单领域事件

public class OrderDomainEvent<T> extends PayloadApplicationEvent<T> {/*** Create a new PayloadApplicationEvent.** @param source  the object on which the event initially occurred (never {@code null})* @param payload the payload object (never {@code null})*/public OrderDomainEvent(Object source, T payload) {super(source, payload);}
}

4.2. 订单领域事件源

public enum OrderDomainEventSource {/*** 销售订单创建事件源*/SALE_ORDER_CREATE("ORDER-SERVICE:ORDER-CREATE-EVENT", "销售订单创建事件"),;/*** 事件原*/private final String source;/*** 事件原描述*/private final String desc;OrderDomainEventSource(String source, String desc) {this.source = source;this.desc = desc;}public String getSource() {return source;}public String getDesc() {return desc;}
}

4.3. 订单领域事件信息载体

public class OrderCreateEventPayload {/*** 订单编号*/private String orderNumber;public String getOrderNumber() {return orderNumber;}public void setOrderNumber(String orderNumber) {this.orderNumber = orderNumber;}@Overridepublic String toString() {return "OrderCreateEventPayload{" +"orderNumber='" + orderNumber + '\'' +'}';}
}

通常在事件中携带信息设计可分两种:

  • 通过唯一标识回查信息
  • 事件载体中携带当前领域对象不可变信息

在实践过程中我通常使用第二种方案,这样可以减少依赖。

4.4. 订单领域事件发布

@Component
public class OrderDomainEventPublisher implements ApplicationEventPublisherAware {private static final LogUtil LOG_UTIL = LogUtil.getLogger(OrderDomainEventPublisher.class);private ApplicationEventPublisher applicationEventPublisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}/*** 订单领域事件发布* @param orderDomainEvent 订单领域事件*/public <T> void publishEvent(OrderDomainEvent<T> orderDomainEvent){OrderDomainEventSource saleOrderDomainEventSource = (OrderDomainEventSource) orderDomainEvent.getSource();LOG_UTIL.info("{} 订单领域事件发布,事件源:{}", DateUtil.date(orderDomainEvent.getTimestamp()), saleOrderDomainEventSource.getDesc());applicationEventPublisher.publishEvent(orderDomainEvent);}
}

4.5. 订单领域事件监听

4.5.1. 监听者1
@Component
public class OrderCreateEventListener1 implements ApplicationListener<OrderDomainEvent<OrderCreateEventPayload>> {private static final LogUtil LOG_UTIL = LogUtil.getLogger(OrderCreateEventListener1.class);@Overridepublic void onApplicationEvent(OrderDomainEvent<OrderCreateEventPayload> event) {OrderDomainEventSource orderCancelEventSource = (OrderDomainEventSource) event.getSource();LOG_UTIL.info("{} 收到订单领域事件,事件发生时间:{},事件源:{}", DateUtil.now(), DateUtil.date(event.getTimestamp()), orderCancelEventSource.getDesc());}
}
4.5.1. 监听者2
@Component
public class OrderCreateEventListener2 implements ApplicationListener<OrderDomainEvent<OrderCreateEventPayload>> {private static final LogUtil LOG_UTIL = LogUtil.getLogger(OrderCreateEventListener2.class);@Overridepublic void onApplicationEvent(OrderDomainEvent<OrderCreateEventPayload> event) {OrderDomainEventSource orderCancelEventSource = (OrderDomainEventSource) event.getSource();LOG_UTIL.info("{} 收到订单领域事件,事件发生时间:{},事件源:{}", DateUtil.now(), DateUtil.date(event.getTimestamp()), orderCancelEventSource.getDesc());}
}
4.5.1. 监听者3
@Component
public class OrderCreateEventListener3 implements ApplicationListener<OrderDomainEvent<OrderCreateEventPayload>> {private static final LogUtil LOG_UTIL = LogUtil.getLogger(OrderCreateEventListener3.class);@Overridepublic void onApplicationEvent(OrderDomainEvent<OrderCreateEventPayload> event) {OrderDomainEventSource orderCancelEventSource = (OrderDomainEventSource) event.getSource();LOG_UTIL.info("{} 收到订单领域事件,事件发生时间:{},事件源:{}", DateUtil.now(), DateUtil.date(event.getTimestamp()), orderCancelEventSource.getDesc());}
}

5. 通过案例来看设计的好处

在工程实践过程中通常面临两种情况:

  • 多个不同领域业务划分为不同的服务;
  • 多个不同领域业务因为时间成本、部署成本、运维成本、业务规模、需求发布节奏等多方面影响,而不得设计为单一服务,多个不同领域业务代码都包含在单一服务内;

从第一种情况来考虑,如果不对代码进行分离设计则会面临以下情况:

  • 不关注的依赖对领域模型的侵入;
  • 分布式事务解决方案对领域模型的侵入;

从第二种情况来考虑,如果不对代码进行分离设计则会面临以下情况:

  • 不关注的依赖对领域模型的侵入;
  • 后续拆分复杂度的增加;

从以上分析不难看出通过事件发布和监听手段对代码进行隔离的好处在于对无关依赖的隔离、对技术复杂度的隔离、方便后续拆分。

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

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

相关文章

Unity引擎:创造无限可能的游戏开发平台

Unity引擎&#xff1a;创造无限可能的游戏开发平台 一、Unity引擎概述1.1 什么是Unity引擎&#xff1f;1.2 Unity引擎的特点和优势 二、Unity开发环境和工具2.1 Unity编辑器2.2 支持的平台2.3 脚本语言2.4 图形和音频工具 三、Unity游戏开发流程四、示例应用场景五、结论&#…

设计模式 -职责链模式

定义 职责链模式是一种对象行为型模式&#xff0c;它常用于处理具有多个处理者的请求。在一个软件系统中&#xff0c;当一个请求可以被多个对象处理时&#xff0c;这些对象可以组织成一条链&#xff0c;并按照这个链传递请求&#xff0c;从而实现了请求发送者和请求处理者之间…

Java JDK 和 JRE 有什么区别

Java JDK 和 JRE 有什么区别 在Java开发中&#xff0c;Java Development Kit&#xff08;JDK&#xff09;和Java Runtime Environment&#xff08;JRE&#xff09;是两个关键的概念&#xff0c;它们之间存在一些区别。 Java Development Kit (JDK)&#xff1a; 用途&#xff1…

HTML h1和h2的三点区别

HTML中的h1和h2 HTML中&#xff0c;h1和h2都是标题元素&#xff0c;二者的关系可以说是递进或递减的关系&#xff0c;如何理解呢&#xff1f;&#xff0c;HTML文档定义的网页一般会被设计成具有一定的结构&#xff0c;而且通常是具有清晰的结构&#xff0c;比如都具有一个主题…

vmware下ubuntu 22.04从主机复制出错

原因分析 vmware下ubuntu 22.04从主机复制出错&#xff0c;如果确定安装了vmware-tools而且重装之后无法解决&#xff0c;大概率是因为vmware-tools和wayland冲突&#xff08;ubuntu 22.04默认使用wayland而不是xorg&#xff09; wayland vs xorg Xorg是X窗口系统的最新版本&…

MySQL-函数

一、统计函数 CREATE TABLE student (id INT NOT NULL DEFAULT 1,name varchar(20) not null default ,chinese float not null default 0.0,english float not null default 0.0,math float not null default 0.0 );insert into student values (1,曹操,77,89,85);insert int…

Vue3中watch和watchEffect的区别及使用场景

Vue3 中 watch 和 watchEffec t的区别及使用场景 在Vue 3中&#xff0c;watch和watchEffect是用于监听响应式数据变化的两个重要API。它们在使用方式和功能上有一些区别&#xff0c;下面将从不同方面进行比较。 1. 使用方式 watch的使用方式是通过在组件选项中定义一个watch…

centos7上源码安装mysql--运维高级

第一步,安装必要的依赖: yum install -y cmake ncurses-devel bison gcc gcc-c make unzip libaio numactl 第二步,创建mysql用户和组: wget http://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.18.tar.gz tar zxvf mysql-5.7.18.tar.gz 第三步,下载MySQL 5.7.18 源码…

postgres在docker中使用

记录个人开发过程中postgres在docker中的使用&#xff0c;以便后续查看。 Dockerfile 个人是在M1电脑上开发&#xff0c;所以platform使用linux/amd64来兼容amd芯片。 FROM --platformlinux/amd64 postgres:16.1-alpine COPY ./poetrydb.sql /docker-entrypoint-initdb.d/po…

Jenkins 如何查看已经记录登录服务器的凭证密码

文章目录 一、背景描述二、解决方案一&#xff08;查看所有账号密码&#xff09;三、解决方案二&#xff08;查询指定账号密码&#xff09; 一、背景描述 在日常的开发过程中&#xff0c;有时候会出现忘记开发、测试服务器的登录密码的情况。此时恰巧 Jenkins 上记录了登录该主…

【笔记】windows+pytorch:部署一下stable diffusion和NeRF

之前都是 *nix 环境使用 pytorch&#xff0c;这次尝试了一下windows。 我们来部署下流行性高的stable diffusion和我觉得实用性比stable diffusion高多了的NeRF Stable Diffusion 其实&#xff0c;我也不知道要写啥&#xff0c;都是按照步骤做就好了&#xff0c;后面等有时间…

如何快速生成项目目录结构树?

经常在网上看到下面这种由一个项目&#xff0c;生成一个结构树&#xff0c;你知道它是怎么生成的吗&#xff1f; 这就是利用本文要介绍的一个工具——Treer&#xff0c;treer就是一款专门用来快速生成目录结构树的命令行工具。 第一步&#xff1a;安装treer 在终端执行全局…

openssl 生成CA及相关证书

生成私钥文件(pem) # 生成私钥 # genrsa&#xff1a;生成RSA秘钥 # 2048&#xff1a;密钥长度为2048比特 # -out&#xff1a;私钥文件路径名 openssl genrsa -out ca_private.pem 2048 # 生成私钥&#xff0c;且加密 # -aes256&#xff1a;使用aes256对私钥进行加密 openssl g…

蓝桥杯第199题 扫地机器人 暴力优化 二分法 简单题 C++

题目 扫地机器人 - 蓝桥云课 (lanqiao.cn)https://www.lanqiao.cn/problems/199/learning/?page1&first_category_id1&name%E6%89%AB%E5%9C%B0%E6%9C%BA%E5%99%A8%E4%BA%BA 思路和解题方法 首先&#xff0c;通过cin语句输入了终点位置n和障碍物数量k。使用一个数组a来…

微服务学习|初识elasticsearch、操作索引库、文档操作、RestClient操作索引库、RestClient操作文档

初识elasticsearch 什么是elasticsearch&#xff1f; elasticsearch是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。 elasticsearch结合kibana、Logstash、Beats&#xff0c;也就是elastic stack (ELK)。被广泛应用在日志数据分析、实…

asn1格式详解

ASN.1&#xff08;Abstract Syntax Notation One&#xff09;是一种用于描述数据结构的标准表示法。它通常用于描述数据交换格式&#xff0c;如证书、密钥和协议消息。ASN.1格式由一系列的类型和值组成&#xff0c;可以使用一些工具和库来解析和处理ASN.1格式的数据。 在C中&a…

python+pytest接口自动化(4)-requests发送get请求

python中用于请求http接口的有自带的urllib和第三方库requests&#xff0c;但 urllib 写法稍微有点繁琐&#xff0c;所以在进行接口自动化测试过程中&#xff0c;一般使用更为简洁且功能强大的 requests 库。下面我们使用 requests 库发送get请求。 requests库 简介 request…

【代码随想录】算法训练计划36

贪心 1、435. 无重叠区间 题目&#xff1a; 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 思路&#xff1a; 贪心&#xff0c;重叠个数&#xff0c;和射气球一样,重叠区间…

C# WPF上位机开发(倒计时软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 生活当中&#xff0c;我们经常会遇到倒计时的场景&#xff0c;比如体育运动的时候、考试的时候等等。正好最近我们学习了c# wpf开发&#xff0c;完…

力扣114. 二叉树展开为链表

思路&#xff1a; 根据二叉树前序遍历&#xff1a;根-左子树-右子树&#xff1b;要按照前序遍历将二叉树展开&#xff0c;则遍历节点右子树需要挂载到左子树“最右”节点右子树上&#xff1b;则当前节点 current 左子树 next current->left 的最右节点 rightmost &#xff…