当前位置: 首页 > article >正文

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;
    }

    @Override
    public 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;

    @Override
    public 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);

    @Override
    public 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);

    @Override
    public 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);

    @Override
    public void onApplicationEvent(OrderDomainEvent<OrderCreateEventPayload> event) {

        OrderDomainEventSource orderCancelEventSource = (OrderDomainEventSource) event.getSource();
        LOG_UTIL.info("{} 收到订单领域事件,事件发生时间:{},事件源:{}", DateUtil.now(), DateUtil.date(event.getTimestamp()), orderCancelEventSource.getDesc());
    }
}

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

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

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

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

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

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

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

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


http://www.kler.cn/news/149702.html

相关文章:

  • 西南科技大学数字电子技术实验二(SSI逻辑器件设计组合逻辑电路及FPGA实现 )预习报告
  • python tkinter 使用(七)
  • 3-Python与设计模式--简单工厂模式
  • Android平台GB28181设备接入模块开发填坑指南
  • C++-多态常见试题的总结
  • 物联网边缘计算是什么?如何实现物联网边缘计算?
  • NX二次开发UF_CURVE_create_combine_curves 函数介绍
  • 工业智能网关如何保障数据通信安全
  • 【华为数通HCIP | 网络工程师】821刷题日记-BFD和VRRP 及重点(1)
  • shopee买家通系统批量注册虾皮买家号的软件
  • Linux基本指令总结(二)
  • 【设计模式】行为型模式-第 3 章第 5 讲【观察者模式】
  • CSS特效020:涌动的弹簧效果
  • thinkphp6生成PDF自动换行
  • Iar 8051等各种版本安装破解过程
  • 基于UI交互意图理解的异常检测方法
  • 基于FPGA的五子棋游戏设计
  • 在线 SQL 模拟器SQL Fiddle使用简介
  • Python3基础
  • 爬虫-基于python的旅游景点推荐系统的设计与实现-计算机毕业设计源码24044
  • Linux加强篇002-部署Linux系统
  • SimpleDateFormat在多线程下的安全问题
  • 392. 判断子序列
  • 【JavaScript】3.3 JavaScript工具和库
  • 【开题报告】基于深度学习的驾驶员危险行为检测系统
  • WebSocket--1.协议解析
  • Pt100 和 Pt1000 传感器:重要事实和差异
  • 纯前端实现导入excel数据
  • 【解决视觉引导多个位置需要标定多个位置的问题】
  • C++ cmake脚本链接Boost库