CppMicroServices 核心概念:动态服务注册与模块化系统
CppMicroServices README 对自身的定位:
“provides adynamic service registryandmodule system”
本文基于Modules/CppMicroServices/core/src/源码解释这两个概念的具体含义。
第一部分 动态服务注册(Dynamic Service Registry)
"动态"是相对"静态绑定"而言的,具体含义可拆成四点。
1. 运行期注册,而非编译期绑定
静态方式(传统 C++):
// 编译期就固定了用哪个实现——换实现要改代码、重新编译MyAlgorithmImpl algo;consumer.use(&algo);动态方式(微服务):
// 提供方:运行期某个时刻才把实现"发布"出去ctx->RegisterService<IAlgorithm>(newMyAlgorithmImpl);// 消费方:运行期按接口名查询,编译期完全不知道实现是谁autoref=ctx->GetServiceReference<IAlgorithm>();IAlgorithm*algo=ctx->GetService(ref);消费方二进制里没有任何对实现类的引用——链接时不需要实现库存在。
2. 服务可以在任意时刻出现和消失
这是"动态"最核心的含义。服务的生命周期不是"程序启动时全部就绪、
退出时全部销毁",而是:
- 一个 .so 被加载 → 它的服务这时才注册进来(
US_INITIALIZE_MODULE静态初始化触发) - 一个 .so 被卸载 → 它的服务自动注销(
ModulePrivate::RemoveModuleResources) - 同一接口的服务可以中途被替换、增加、移除
所以消费方必须面对的现实是:“我要的服务现在可能有、可能没有、待会儿可能变”。
这正是ServiceTracker存在的原因——它不是一次性查询,而是持续跟踪:
// AddingService / RemovedService 回调应对服务的动态来去// QmitkAbstractView.cpp: L146 实际用例ctkServiceTracker<mitk::IDataStorageService*>m_DataStorageServiceTracker;3. 查询是按"接口名字符串 + 属性过滤"的晚绑定
注册表内部就是一张接口名到实现列表的映射(usServiceRegistry_p.h: L63-80):
// 接口名(字符串) → 按 ranking 排序的实现列表MapClassServices classServices;查找发生在调用那一刻(Get返回当前 ranking 最高者),而不是链接那一刻。
配合 LDAP 过滤器还能按属性筛选:
GetServiceReferences("IAlgorithm","(type=fast)");同一接口多个实现共存、高 ranking 者胜出、随时可被更高 ranking 的新实现顶替
——这些都是静态绑定做不到的。
4. 事件驱动的感知
每次注册/注销都会广播ServiceEvent(REGISTERED / UNREGISTERING),
监听者实时收到通知(usServiceRegistry.cpp: L128-133)。
系统各部分对"服务拓扑的变化"是被动感知、自动适应的,不需要轮询或重启。
在 MITK 中的实际意义
以文件读取为例:MITK Core 定义IFileReader接口;DICOM 模块加载时注册
DICOM 读取器,NRRD 模块加载时注册 NRRD 读取器。打开文件时 Core 按 MIME type
查注册表选用合适的 reader——Core 编译时完全不知道世界上有哪些格式。
用户装一个新格式插件,不用改一行 Core 代码,读取能力就"长"出来了。
一句话总结
动态服务注册 = 把"接口→实现"的绑定从编译期/链接期推迟到运行期,
并允许这个绑定关系在程序运行过程中随模块加载卸载而随时变化。
第二部分 模块化系统(Module System)
模块化系统指的是:把动态库从"一堆被动的代码集合"升级为
有身份、有元数据、有生命周期、有资源、可被管理的一等公民。
对照源码,具体含义可拆成五点。
1. 模块是有"身份"的实体,而非匿名的 .so
普通 C++ 程序里,动态库加载后就"融化"在进程里——没有名字、没有边界、
无法枚举。而 CppMicroServices 给每个库一个运行期身份:
// usModuleRegistry.cpp: L39-63 —— 进程级模块注册表typedefUS_UNORDERED_MAP_TYPE<std::string,Module*>ModuleMap;// name → Module*US_GLOBAL_STATIC_WITH_DELETER(ModuleMap,modules,ModuleDeleter)每个模块有唯一自增 id、名字、磁盘路径(ModuleInfo),可以按名查询、全量枚举:
Module*m=ModuleRegistry::GetModule("MitkCore");// 按名找模块std::vector<Module*>all=ModuleRegistry::GetModules();// 枚举所有模块2. 模块有显式的生命周期状态机
普通 .so 只有"加载/未加载"两态且不可感知。模块系统给出完整生命周期,
且每次状态迁移都广播事件(usModule.cpp: L119-171):
注册 → LOADING → (Activator::Load) → LOADED → UNLOADING → (Activator::Unload) → UNLOADED- Activator 钩子:模块加载/卸载时有确定的回调时机
(ModuleActivator::Load/Unload),初始化逻辑有了落脚点,
不再依赖脆弱的全局对象构造顺序 - ModuleEvent:其他模块可监听"谁被加载了/卸载了"并做出反应
3. 每个模块有自己的"视角"——ModuleContext
ModuleContext(usModuleContext.cpp)是模块与框架交互的专属句柄
——注册服务、查服务、挂监听器都通过它。关键在于框架记得每笔操作是哪个模块做的:
// usModulePrivate.cpp: L118-146 —— 正因为记账到模块头上,卸载时才能自动清理coreCtx->services.GetRegisteredByModule(this,srs);// 这个模块注册过什么coreCtx->services.GetUsedByModule(q,srs);// 这个模块用过什么这就是"模块化"与"全局大杂烩"的本质区别:资源归属清晰,卸载即自动回收,
不会留下悬空的服务和监听器。
4. 模块自带元数据与资源
- manifest.json(
usModulePrivate.cpp: L53-68):每个模块可内嵌一份清单,
声明版本、自动加载目录等属性,框架加载时解析 - 嵌入式资源系统(
usModuleResource/ModuleResourceContainer):
文件可以编译进模块二进制,运行期用统一 API 读取——BlueBerry 读plugin.xml、
布局选择器读mxnLayout_*.json预设用的就是这套机制:
// QmitkMultiWidgetLayoutSelectionWidget.cpp: L42 —— 从模块资源里找预设布局us::GetModuleContext()->GetModule()->FindResources("/","mxnLayout_*.json",false);5. 模块间的耦合被压缩到"服务接口"这一个通道
模块系统与动态服务注册是配套的:模块是服务的提供者和消费者单位,
模块间不直接 include 对方的实现、不直接链接对方的内部符号,
只通过服务接口交互。于是:
- 模块可独立开发、独立编译、独立替换
- 依赖关系 = “我需要哪些服务接口”,而不是"我链接哪些库"
一句话总结
模块化系统 = 让动态库变成可枚举、可寻址、有生命周期钩子、
有元数据和私有资源、按模块记账并自动回收资源的运行期管理单元
——它提供"秩序"(谁在场、谁负责、何时初始化、何时清理),
动态服务注册提供"通信"(模块间怎么解耦地互相使用),
二者合起来才构成 OSGi 式的动态软件栈。
关键源码文件索引
| 概念 | 文件(Modules/CppMicroServices/core/src/下) | 关键行 |
|---|---|---|
| 模块注册表 | module/usModuleRegistry.cpp | L39-63(ModuleMap);L75(Register) |
| 模块生命周期 | module/usModule.cpp | L119(Start);L173(Stop) |
| Activator 接口 | ../include/usModuleActivator.h | Load/Unload |
| 模块上下文 | module/usModuleContext.cpp | L78(RegisterService);L98(GetServiceReference) |
| 按模块记账/清理 | module/usModulePrivate.cpp | L118-146(RemoveModuleResources) |
| manifest.json 解析 | module/usModulePrivate.cpp | L53-68 |
| 模块资源系统 | module/usModuleResource.cpp/usModuleResourceContainer.cpp | - |
| 服务注册表 | service/usServiceRegistry.cpp | L87(RegisterService);L128-133(事件广播) |
| 服务注册表结构 | service/usServiceRegistry_p.h | L63-80(两张映射表) |
| LDAP 过滤器 | module/usLDAPExpr.cpp | - |
| 自动注册宏 | ../include/usModuleInitialization.h | L57-120(US_INITIALIZE_MODULE) |
| MITK 实际用例 | Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp | L146(ServiceTracker) |
| MITK 实际用例 | Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp | L42(FindResources) |