拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

java-design-patterns 仓库 Data Bus(数据总线)模式解析:基于消息类型驱动的集中式组件通信

java-design-patterns 仓库 Data Bus(数据总线)模式解析:基于消息类型驱动的集中式组件通信
  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

导读

本文以 java-design-patterns 仓库中data-bus模块(data-bus/README.md)为核心,系统讲解 Data Bus(数据总线,又称 Event Bus / Message Bus)设计模式。该模式为系统各组件提供一个集中式通信通道,组件之间无需直接互相引用即可交换数据,从而获得低耦合、易扩展、易维护的架构收益。读完本文,你将掌握 Data Bus 模式的核心思想、Java 实现结构、消息按类型选择性分发的机制,以及它在事件驱动、微服务等场景下的适用边界与取舍。

Data Bus 模式是什么

Data Bus 设计模式的目标是:提供一个集中式通信通道,让系统中各种组件无需直接连接即可交换数据,从而促进松耦合,并提升系统的可扩展性与可维护性。

  • 别名:Event Bus(事件总线)、Message Bus(消息总线)。
  • 一句话概括:Data Bus 是一种"根据被传递的消息或事件类型来连接应用组件"的设计模式——组件只按类型接收自己关心的消息,互相之间不产生直接依赖。

现实类比:机场广播系统

大型机场中有航空公司、乘客、行李搬运工、安检人员等多类实体,它们都需要互通信息。若让每个实体直接与其他实体通信,关系网会极其复杂。机场实际采用的做法是:使用一套**集中式广播系统(即 Data Bus)**统一发布航班信息、安检警报等关键更新,各实体只监听与自己相关的消息。这样既解耦了通信过程,又能无破坏地接入新实体,与 Data Bus 在 Java 中的定位一致。

消息分发流程

程序化示例:在线活动报名应用

假设你有一个支持在线预订和参与活动的应用,希望把活动广告这类通知发送给社区普通成员,但不发给活动管理员/组织者;而管理员则需要收到"新广告已发送给全体成员"这类时机通知。Data Bus 让两类成员各自只接受特定类型的消息,双方互不知晓对方类别的存在,只知道"正在传递的消息类型"。

仓库中该模块源码位于>public interface Member extends Consumer<DataType> { void accept(DataType event); }

2. 定义事件数据类型 DataType 与 AbstractDataType

通过总线发布的数据必须实现 DataType 接口,它承载"事件在哪条总线上发送"的回指引用:

public interface DataType { DataBus getDataBus(); void setDataBus(DataBus dataBus); }

AbstractDataType 使用 Lombok 的@Getter/@Setter提供默认实现,具体事件类型只需继承它并携带自己的业务字段即可:

@Getter @Setter public class AbstractDataType implements DataType { private DataBus dataBus; }

仓库内置了三种事件类型(位于>public class DataBus { private static final DataBus INSTANCE = new DataBus(); private final Set<Member> listeners = new HashSet<>(); public static DataBus getInstance() { return INSTANCE; } public void subscribe(final Member member) { this.listeners.add(member); } public void unsubscribe(final Member member) { this.listeners.remove(member); } public void publish(final DataType event) { event.setDataBus(this); listeners.forEach( listener -> listener.accept(event)); } }

要点说明:

  • 单例:DataBus.getInstance()返回全局唯一实例(源码注释明确标注该实现使用 Singleton)。
  • publish的第一步是event.setDataBus(this),把事件回指到当前总线——这正是StatusMember能在收到事件后"反向通过总线再发消息"的前提(见下文)。
  • 同步语义:从App.java的类注释可知,这是一个同步数据总线——publish方法在所有成员都接收并处理完事件之后才返回。
  • 多对多:任意数量的发布者可以向总线发数据,任意数量的成员接收数据;所有成员收到的是同一份数据,各成员收到数据的先后顺序是实现的细节(HashSet不保证顺序)。

4. 按类型选择性接收的成员实现

普通成员MessageCollectorMember 只处理MessageData类型的消息,其他类型直接忽略:

public class MessageCollectorMember implements Member { private final String name; private final List<String> messages = new ArrayList<>(); public MessageCollectorMember(String name) { this.name = name; } @Override public void accept(final DataType data) { if (data instanceof MessageData) { handleEvent((MessageData) data); } } }

handleEvent内部通过LOGGER.info("{} sees message {}", name, data.getMessage())打印日志并把消息存入messages列表;getMessages()返回不可变副本List.copyOf(messages),防止外部修改内部状态。

管理员/组织者StatusMember 则处理StartingData与StoppingData两类事件:

public class StatusMember implements Member { private final int id; private LocalDateTime started; private LocalDateTime stopped; public StatusMember(int id) { this.id = id; } @Override public void accept(final DataType data) { if (data instanceof StartingData) { handleEvent((StartingData) data); } else if (data instanceof StoppingData) { handleEvent((StoppingData) data); } } }

值得注意的源码细节:当StatusMember收到StoppingData(应用停止)时,它会借助事件上回指的getDataBus()反向发布一条告别消息:

private void handleEvent(StoppingData data) { stopped = data.getWhen(); LOGGER.info("Receiver {} sees application stopping at {}", id, stopped); LOGGER.info("Receiver {} sending goodbye message", id); data.getDataBus().publish(MessageData.of(String.format("Goodbye cruel world from #%d!", id))); }

这说明 Data Bus 中的成员既是消费者,也可以作为发布者,成员之间通过总线形成"环形消息流",而彼此仍不知道对方的存在。

5. 组合演示 App

App.java 串联了完整流程:订阅 → 发布启动事件 → 发布消息 → 中途订阅新成员 → 退订旧成员 → 发布停止事件:

class App { public static void main(String[] args) { final var bus = DataBus.getInstance(); bus.subscribe(new StatusMember(1)); bus.subscribe(new StatusMember(2)); final var foo = new MessageCollectorMember("Foo"); final var bar = new MessageCollectorMember("Bar"); bus.subscribe(foo); bus.publish(StartingData.of(LocalDateTime.now())); } }

当总线发布StartingData时,输出如下:

02:33:57.627 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 2 sees application started at 2022-10-26T02:33:57.613529100 02:33:57.633 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 1 sees application started at 2022-10-26T02:33:57.613529100

如上所示,MessageCollectorMember只接受MessageData,因此看不到StartingData/StoppingData这两类只有StatusMember(管理员/组织者)可见的事件。这种按消息类型的选择性分发,恰好阻止了普通成员收到管理类通知。

如果沿 App 的完整执行序列继续运行(发布MessageData.of("Only Foo should see this")、订阅 bar、发布MessageData.of("Foo and Bar should see this")、退订 foo、发布MessageData.of("Only Bar should see this")、发布StoppingData),可以看到:普通成员按订阅/退订状态动态接收消息,而StatusMember收到停止事件后又向总线回发告别消息。

6. 测试印证

DataBusTest.java 用 Mockito 直接验证了总线契约:

  • publishedEventIsReceivedBySubscribedMember:订阅后发布事件,then(member).should().accept(event)断言订阅者收到事件;
  • publishedEventIsNotReceivedByMemberAfterUnsubscribing:退订后发布事件,断言成员不再收到。

MessageCollectorMemberTest 与 StatusMemberTest 则分别验证了各成员只处理自己关心的消息类型,测试与源码共同构成了"类型选择性分发"行为的证据链。

何时使用 Data Bus 模式

符合以下情况时,Data Bus 是合适的选择:

  • 多个组件需要共享数据或事件,但不希望直接耦合;
  • 复杂的事件驱动系统,其中信息流动随运行动态变化;
  • 分布式系统,组件可能部署在不同环境中;
  • 微服务架构下的服务间通信。

Data Bus 模式的真实应用场景

  • 大规模应用中的事件处理系统;
  • 微服务架构中的服务间通信;
  • 实时数据处理系统(如股票交易平台);
  • 类似 Spring 的框架生态,特别是其application event(应用事件)机制。

优点与权衡

优点(Benefits)

  • 松耦合:组件交互不产生直接依赖;
  • 灵活:新增订阅者或发布者无需影响既有组件;
  • 可扩展:各组件可独立扩展;
  • 可复用:总线和组件可在不同系统中复用。

代价(Trade-offs)

  • 复杂度:引入总线会给系统架构增加一层复杂度;
  • 性能开销:额外的通信层可能引入延迟;
  • 调试困难:在事件繁多的系统中,追踪数据流会比较困难。

与其他设计模式的关系

  • Mediator(中介者):同样促进组件间通信,但 Mediator 把控制逻辑集中起来;Data Bus 则把控制分散到总线与各成员之间。
  • Observer(观察者):Data Bus 在本质上是发布-订阅机制,与 Observer 通知多个对象状态变化的方式相似。从 App.java 的注释也可以看到,Data Bus 与观察者模式相似——成员在总线上注册后接收发布的每份数据,并可自行决定是否响应某条消息。
  • Publish/Subscribe(发布-订阅):Data Bus 常以发布-订阅机制实现——发布者向总线投递消息而无需了解订阅者是谁。

参考资料

本文内容以>

  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

相关推荐

上一篇:Flutter Assets Audio Player与后台服务集成:实现后台播放和锁屏控制
下一篇:BTCPay Server视频行业:订阅支付方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表