模块模式不抽取元数据,而是通过清晰接口契约与职责边界使行为元数据自然浮现、可识别、可组装;其本质是设计出来的可执行契约,依赖模块声明、强语义接口和高内聚实现来保障元数据的确定性与可靠性。

直接说结论:模块模式本身不“抽取”元数据,而是通过明确的接口契约和职责边界,让行为元数据自然浮现、可识别、可组装。关键不在“抽”,而在“定义得清、暴露得准、组合得稳”。
行为元数据不是挖出来的,是设计出来的
所谓“行为元数据”,指能被系统自动识别、调度或配置的功能描述信息,比如“这个模块支持登录”“该组件响应用户点击并跳转到个人页”“此服务在Token过期时触发刷新”。它不是代码注释或文档,而是嵌入在模块结构中的可执行契约。
要做到这点,需满足三个前提:
-
每个模块只声明一个核心能力,例如
user-auth模块只暴露IAuthHandler接口,不混入头像裁剪或消息推送逻辑 -
接口方法名与语义强一致,如
attemptLogin(credential)而非doSomething(Obj),便于工具静态分析生成元数据 -
模块声明自身能力范围,鸿蒙用
@ModuleApi,Android Atlas 用remote-interface模块,Java 9+ 用provides ... with,都是显式标注“我能做什么”
用模块声明+接口协议生成可运行的元数据
真正起作用的是模块与其对外接口的绑定关系。以 Atlas 框架为例:
- 定义
LoginService接口在auth-api模块中,仅含login()、logout()、isAuthenticated() -
auth-impl模块实现它,并在bundle.json中声明:"exports": ["com.example.auth.LoginService"] - 宿主应用启动时,Bundle Framework 扫描所有已加载 Bundle 的 exports 列表,自动生成一张行为映射表:
{"LoginService": {"module": "auth-impl", "methods": ["login", "logout", "isAuthenticated"], "trigger": "onUserClick"}}
这张表就是动态构建出的行为元数据——它不依赖反射扫描私有方法,也不靠运行时试探调用,而是由模块结构和接口定义共同保证的确定性输出。
高内聚模块天然携带可验证的行为标签
内聚度越高,行为越单一,元数据就越精准、越少歧义。比如:
- 一个叫
payment-gateway的模块,如果同时包含“微信支付”“订单创建”“库存扣减”“短信通知”,那它的行为元数据就会混乱、不可靠 - 而拆成
payment-wechat(只做微信通道)、order-factory(只构造订单对象)、inventory-locker(只处理分布式锁),每个模块导出的接口都对应唯一动词:submitToWechat()、buildOrder()、tryLockSku() - 这时,自动化工具就能准确标记:
submitToWechat → requires: network, permission: INTERNET, sideEffect: external_redirect
这种标签不是人工填写的配置项,而是从模块名、接口名、参数类型、异常声明中推导出的语义事实。
避免常见陷阱:别把“动态”当成“随意”
动态抽取不等于放弃约束。以下做法会破坏元数据可靠性:
- 在模块内部用
if-else根据字符串参数切换行为(如handle("login")或handle("logout")),导致元数据无法静态识别具体能力 - 跨模块直接 new 实例或访问 package-private 类,绕过接口层,使行为脱离契约管理
- 把元数据硬编码在 JSON 配置里,与真实接口不同步,久而久之变成“文档式谎言”
真正的动态性,来自模块注册/卸载机制(如 Atlas 的 startInstall / deactivate)与接口发现机制(如 Java 的 ServiceLoader 或鸿蒙的 AbilitySliceManager),它们让元数据随模块生命周期实时更新,而不是靠猜或配。

















