Java 数据总线模式(Data Bus Pattern)深度解析:以 java-design-patterns 仓库为例

发布时间:2026/10/3 2:24:37
Java 数据总线模式(Data Bus Pattern)深度解析:以 java-design-patterns 仓库为例 示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载数据总线模式Data Bus Pattern又称 Event Bus / Message Bus通过一条集中的消息通道让应用中的各个组件按消息类型收发事件组件之间互不感知、彻底解耦。本指南以 java-design-patterns 仓库中的data-bus模块为实证蓝本从模式含义、类结构、完整源码实现、测试验证到实战扩展逐步拆解读完你将掌握如何在 Java 应用中实现一个支持多对多通信、成员可选择性订阅事件的数据总线。模式含义让组件只认“消息类型”数据总线模式允许一个应用程序的组件之间收发消息/事件而不需要这些组件相互感知——它们只需要知道所发送/接收的消息/事件的类型即可。它本质上就是常说的 Event-Bus 消息总线模式属于架构型Architectural设计模式核心标签是解耦Decoupling。仓库中data-bus模块的英文 README 给出了更精确的意图描述提供一个集中的通信通道让系统中各个组件无需直接连接即可交换数据从而促进松耦合并提升系统的可扩展性与可维护性。用一句话概括数据总线把“组件之间的直接通信”替换为“组件与总线之间的通信”组件只按消息类型决定自己是否接收彼此之间零依赖。在 java-design-patterns 的data-bus实现中DataBus采用单例SingletonMember成员注册到总线后即可接收发布到总线上的每一份数据成员可以自行决定对任何给定消息做出反应或忽略参见 DataBus.java 与 App.java 的类注释。该实现是一个同步数据总线当数据发布到总线时publish方法在所有成员接收完数据并返回之前不会返回。现实类比机场广播系统可以把一座大型机场想象成数据总线模式的现实映射航空公司、乘客、行李搬运人员、安保人员都需要沟通与共享信息但并非每个实体都与其他所有实体直接对话而是通过一个集中的广播系统即数据总线发布航班信息、安全警报等关键更新各实体只收听与自己相关的消息。这样的设计把通信过程解耦每个实体只收到自己需要的信息同时系统可以平滑地接入新实体而不影响既有成员——这正是数据总线模式在 Java 中推动集中式通信与事件处理、提升可扩展性与可维护性的方式。适用场景可以在以下场景使用数据总线模式由组件自己决定接收哪些信息/事件成员通过instanceof类型判断选择性消费消息而非被动接收一切。需要实现多对多的通信一个总线可以有任意数量的发布者也可以有任意数量的接收成员所有成员都会收到同一份数据。希望组件之间不需要感知彼此发布者不知道谁在订阅订阅者也不知道谁在发布双方只依赖消息类型。英文 README 进一步补充了适用面更广的判断标准多个组件需要共享数据或事件但直接耦合不可取复杂事件驱动系统中信息流动态变化分布式系统中组件可能部署在不同环境微服务架构中服务间通信。模块结构与类图data-bus模块的代码组织结构如下完整结构见>/** Members receive events from the Data-Bus. */ public interface Member extends ConsumerDataType { void accept(DataType event); }见 Member.javaDataType接口要求每个事件都能“知道”自己所在的或即将发布的总线以支持事件在接收处理过程中反向向总线再次发布消息后续StatusMember的 goodbye 消息会用到这一能力/** Events are sent via the Data-Bus. */ public interface DataType { DataBus getDataBus(); void setDataBus(DataBus dataBus); }见 DataType.javaAbstractDataType使用 Lombok 的Getter/Setter为所有具体事件提供dataBus字段的默认实现让事件类型可以自由扩展见 AbstractDataType.java。三种事件类型data包中提供了三个示例DataType实现事件类承载字段静态工厂方法语义MessageDataString messagefinalMessageData.of(String message)字符串消息事件StartingDataLocalDateTime whenfinalStartingData.of(LocalDateTime when)应用启动事件携带启动时间StoppingDataLocalDateTime whenfinalStoppingData.of(LocalDateTime when)应用停止事件携带停止时间三者均继承AbstractDataType并通过静态工厂方法of(...)构造源码见 MessageData.java、StartingData.java、StoppingData.java。总线核心DataBus 单例的订阅与发布DataBus是模式的中枢代码精简但职责清晰完整实现见 DataBus.javapublic class DataBus { private static final DataBus INSTANCE new DataBus(); private final SetMember 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)); } }三个方法对应模式的三个基本操作实现要点如下单例获取INSTANCE在类加载时创建通过getInstance()全局共享同一个总线实例保证所有组件接入同一通信中枢。订阅subscribe(member)将成员加入HashSet监听器集合。使用Set意味着同一成员重复订阅会被去重且成员间无固定顺序——文档也明确指出“所有成员都会收到同一份数据但每个成员收到某条数据的顺序是实现细节”。退订unsubscribe(member)从集合中移除成员之后该成员不再收到任何事件。发布publish(event)先把事件自身的dataBus指针指向当前总线这正是StoppingData能反向发布 goodbye 消息的前提再遍历全部监听器依次调用accept(event)。这是一个同步、阻塞的发布过程所有成员处理完毕前publish不会返回。关键调用链与测试验证DataBusTest见 DataBusTest.java用 Mockito 验证了订阅/退订的核心契约Test void publishedEventIsReceivedBySubscribedMember() { // given final var dataBus DataBus.getInstance(); dataBus.subscribe(member); // when dataBus.publish(event); // then then(member).should().accept(event); } Test void publishedEventIsNotReceivedByMemberAfterUnsubscribing() { // given final var dataBus DataBus.getInstance(); dataBus.subscribe(member); dataBus.unsubscribe(member); // when dataBus.publish(event); // then then(member).should(never()).accept(event); }两条测试用例分别断言订阅后的成员一定能收到发布的事件退订后的成员一定收不到后续事件。这正是publish中listeners.forEach(listener - listener.accept(event))的直接行为证明。成员实现按类型选择性接收消息成员决定接收哪些消息的方式是数据总线模式的精髓——每个成员在自己的accept(DataType data)中用instanceof判断消息类型只处理自己关心的类型。普通成员MessageCollectorMember普通社区成员只接受MessageData类型的消息把收到的字符串收集进内部列表见 MessageCollectorMember.javaSlf4j public class MessageCollectorMember implements Member { private final String name; private final ListString messages new ArrayList(); public MessageCollectorMember(String name) { this.name name; } Override public void accept(final DataType data) { if (data instanceof MessageData) { handleEvent((MessageData) data); } } private void handleEvent(MessageData data) { LOGGER.info({} sees message {}, name, data.getMessage()); messages.add(data.getMessage()); } public ListString getMessages() { return List.copyOf(messages); } }注意getMessages()返回的是List.copyOf(...)生成的不可变副本外部无法篡改已收集的消息列表。状态成员StatusMember事件管理员/组织者只接受StartingData与StoppingData两类消息记录应用的启动/停止时间见 StatusMember.javaGetter Slf4j RequiredArgsConstructor public class StatusMember implements Member { private final int id; private LocalDateTime started; private LocalDateTime stopped; Override public void accept(final DataType data) { if (data instanceof StartingData) { handleEvent((StartingData) data); } else if (data instanceof StoppingData) { handleEvent((StoppingData) data); } } private void handleEvent(StartingData data) { started data.getWhen(); LOGGER.info(Receiver {} sees application started at {}, id, started); } 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))); } }StatusMember有一个值得注意的反向发布能力当收到StoppingData时它利用事件上的data.getDataBus()拿到当前总线引用再向总线发布一条MessageDataGoodbye cruel world from #N!。这生动演示了成员既是订阅者、也可以是发布者且二者无需知道彼此的存在——这正是多对多通信的体现。StatusMemberTest见 StatusMemberTest.java验证了其类型选择性传入StartingData会正确记录started时间传入StoppingData会正确记录stopped时间传入MessageData时started与stopped均保持为null——说明状态成员确实忽略不属于自己关注类型的消息。完整运行示例App 演示与输出解读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())); bus.publish(MessageData.of(Only Foo should see this)); bus.subscribe(bar); bus.publish(MessageData.of(Foo and Bar should see this)); bus.unsubscribe(foo); bus.publish(MessageData.of(Only Bar should see this)); bus.publish(StoppingData.of(LocalDateTime.now())); } }运行流程可以拆解为五个阶段订阅两个StatusMemberid1、2与MessageCollectorMember(Foo)先后注册到总线。发布启动事件StartingData广播出去只有两个StatusMember响应记录并打印启动时间Foo 因类型不符而无感知。发布仅 Foo 可见的消息此时 Bar 尚未订阅只有 Foo 收到并收集 Only Foo should see this。Bar 订阅后发布共享消息Foo and Bar should see this 被 Foo 与 Bar 同时收集。Foo 退订后发布仅 Bar 消息Foo 已unsubscribe只有 Bar 收到 Only Bar should see this。发布停止事件两个StatusMember记录停止时间并各自向总线反向发布一条MessageDatagoodbye 消息——此时仍在订阅中的 Foo、Bar 会收集到这些消息。当数据总线发布消息时运行输出大致如下时间戳因运行时刻而异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可见从而防止普通成员收到管理员的运行状态通知。这正是“选择性消息处理”的直观体现。运行方式data-bus是标准 Maven 模块与仓库其他模块共用根目录的pom.xml与 Maven Wrapper。可以执行以下命令在模块内运行演示程序与测试仓库为只读以下均为本地查看/运行方式# 运行演示程序 ./mvnw -pl>赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐深入解析 Java 双缓冲模式Double Buffer Pattern以 java-design-patterns 仓库为例深入解析 Java 双缓冲模式Double Buffer Pattern以 java design patterns 仓库为例 双缓冲Double Bu示例工程教程Java 数据局部性模式Data Locality Pattern深入解析以 java-design-patterns 源码为实例Java 数据局部性模式Data Locality Pattern深入解析以 java design patterns 源码为实例 本指南基于 java示例工程教程Java 领域模型模式Domain Model Pattern实战指南以 java-design-patterns 仓库为例Java 领域模型模式Domain Model Pattern实战指南以 java design patterns 仓库为例 领域模型模式Domain示例工程教程上一篇ng-zorro-antd Flex 布局指令nz-flex完全指南属性 API、间距与对齐实践下一篇Cocos Spine 骨骼动画换装从插槽换装到性能缓存的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询