跨子模块通信实现咨询:回调/监听器及广播方案示例
嘿,这个问题问得很实在!跨子模块通信在Java项目里完全可行,回调/监听器是最常用的方案之一,广播也能轻松实现,下面我给你一步步拆解并附上可运行的代码示例。
这种方式的核心是定义一个公共接口让两个模块对接,只要模块之间有合理的依赖关系(比如A依赖B,或者双方依赖同一个公共API模块),就能顺畅通信。
1. 先定义回调接口
首先得有一个双方都能访问到的回调接口——最好放在公共的API包下(比如你有父模块的话就放父模块的api目录,要是是单向依赖,比如submodule2依赖submodule1,那就把接口放在submodule1里)。
// 示例:放在submodule1的com.example.api包下 public interface SecretCallback { // 定义回调方法,参数根据你的业务需求调整 void onSecretDataReceived(String secretData); }
2. 在SecretService里添加回调注册和触发逻辑
修改submodule1里的SecretService,让它能接收外部注册的回调,并且在数据就绪时通知所有注册的对象:
package com.example.submodule1.service; import com.example.api.SecretCallback; import java.util.ArrayList; import java.util.List; public class SecretService { // 用列表存所有注册的回调,支持一对多通知 private final List<SecretCallback> callbacks = new ArrayList<>(); // 注册回调的方法 public void registerCallback(SecretCallback callback) { if (!callbacks.contains(callback)) { callbacks.add(callback); } } // 别忘了加取消注册的方法,避免内存泄漏! public void unregisterCallback(SecretCallback callback) { callbacks.remove(callback); } // 模拟获取秘密数据的业务方法,拿到数据后触发回调 public void fetchSecretData() { // 这里替换成你的实际业务逻辑:比如从DB/外部接口拿数据 String secretData = "Top Secret: 12345"; // 遍历所有回调,通知数据更新 for (SecretCallback callback : callbacks) { callback.onSecretDataReceived(secretData); } } }
3. 在AwesomeManager里实现回调并注册
在submodule2的AwesomeManager中,实现刚才的回调接口,然后把自己注册到SecretService里:
package com.example.submodule2.manager; import com.example.api.SecretCallback; import com.example.submodule1.service.SecretService; public class AwesomeManager implements SecretCallback { // 构造方法里完成注册,也可以单独写个init方法 public AwesomeManager(SecretService secretService) { secretService.registerCallback(this); } @Override public void onSecretDataReceived(String secretData) { // 这里写你收到数据后的业务逻辑 System.out.println("AwesomeManager 收到秘密数据:" + secretData); // 比如更新缓存、触发UI刷新、调用其他服务等等 } }
4. 测试一下
写个启动类验证效果:
public class MainApp { public static void main(String[] args) { SecretService secretService = new SecretService(); AwesomeManager awesomeManager = new AwesomeManager(secretService); // 触发SecretService获取数据,此时AwesomeManager会收到回调 secretService.fetchSecretData(); } }
注意事项
- 依赖配置:要确保submodule2能访问到SecretService和SecretCallback,比如在submodule2的pom.xml里加submodule1的依赖。
- 内存泄漏:当AwesomeManager不再需要时,一定要调用
unregisterCallback,不然SecretService会一直持有它的引用,导致内存泄漏。
如果你的场景是一对多、多对多,或者不想让模块之间有直接依赖,广播/事件总线是更好的选择。这里给你两种实现方式:自己写简单的广播器,或者用成熟的框架(比如Guava EventBus)。
方式1:自己实现简单广播机制
1. 定义事件类
先在公共API包下定义一个事件类,用来传递数据:
public class SecretEvent { private final String secretData; public SecretEvent(String secretData) { this.secretData = secretData; } // 提供 getter 让订阅者拿数据 public String getSecretData() { return secretData; } }
2. 实现事件总线(广播器)
写一个单例的事件总线,用来管理订阅者和发布事件:
import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; public class EventBus { // 单例模式,全局唯一 private static final EventBus INSTANCE = new EventBus(); // 存储SecretEvent的订阅者 private final List<Consumer<SecretEvent>> secretEventListeners = new ArrayList<>(); private EventBus() {} public static EventBus getInstance() { return INSTANCE; } // 注册订阅者 public void registerSecretEventListener(Consumer<SecretEvent> listener) { if (!secretEventListeners.contains(listener)) { secretEventListeners.add(listener); } } // 取消订阅 public void unregisterSecretEventListener(Consumer<SecretEvent> listener) { secretEventListeners.remove(listener); } // 发布事件,通知所有订阅者 public void publishSecretEvent(SecretEvent event) { for (Consumer<SecretEvent> listener : secretEventListeners) { listener.accept(event); } } }
3. SecretService发布事件
修改SecretService,当数据就绪时发布事件:
package com.example.submodule1.service; import com.example.api.EventBus; import com.example.api.SecretEvent; public class SecretService { public void fetchSecretData() { String secretData = "Top Secret: 67890"; // 发布事件 EventBus.getInstance().publishSecretEvent(new SecretEvent(secretData)); } }
4. AwesomeManager订阅事件
在AwesomeManager里订阅事件,处理数据:
package com.example.submodule2.manager; import com.example.api.EventBus; import com.example.api.SecretEvent; public class AwesomeManager { public AwesomeManager() { // 订阅SecretEvent,用Lambda表达式处理事件 EventBus.getInstance().registerSecretEventListener(event -> { System.out.println("AwesomeManager 通过广播收到秘密数据:" + event.getSecretData()); // 这里写业务逻辑 }); } }
方式2:用Guava EventBus(更简洁)
如果项目里已经用了Guava,直接用它的EventBus会省很多事,代码更简洁:
1. 添加Guava依赖(Maven示例)
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> </dependency>
2. SecretService发布事件
package com.example.submodule1.service; import com.google.common.eventbus.EventBus; public class SecretService { private final EventBus eventBus; public SecretService(EventBus eventBus) { this.eventBus = eventBus; } public void fetchSecretData() { String secretData = "Top Secret: Guava EventBus"; // 发布事件 eventBus.post(new SecretEvent(secretData)); } }
3. AwesomeManager订阅事件
用@Subscribe注解标记订阅方法,非常方便:
package com.example.submodule2.manager; import com.google.common.eventbus.Subscribe; public class AwesomeManager { public AwesomeManager(EventBus eventBus) { // 注册自己到EventBus eventBus.register(this); } // 用@Subscribe注解标记订阅方法,参数是要监听的事件类 @Subscribe public void onSecretEventReceived(SecretEvent event) { System.out.println("AwesomeManager 通过Guava EventBus收到数据:" + event.getSecretData()); } }
4. 测试示例
import com.google.common.eventbus.EventBus; public class MainApp { public static void main(String[] args) { EventBus eventBus = new EventBus(); SecretService secretService = new SecretService(eventBus); AwesomeManager awesomeManager = new AwesomeManager(eventBus); secretService.fetchSecretData(); } }
- 回调/监听器:适合一对一或者少量一对多的场景,模块之间依赖关系明确,实现简单直接,性能也不错。
- 广播/事件总线:适合一对多、多对多的场景,模块之间解耦性更高,用成熟框架的话开发效率拉满。
两种方案都完全可行,选哪个就看你的业务场景和模块依赖设计啦~
内容的提问来源于stack exchange,提问作者MaaAn13

