
CppMicroServices 核心概念动态服务注册与模块化系统CppMicroServices README 对自身的定位“provides adynamic service registryandmodule system”本文基于Modules/CppMicroServices/core/src/源码解释这两个概念的具体含义。第一部分 动态服务注册Dynamic Service Registry动态是相对静态绑定而言的具体含义可拆成四点。1. 运行期注册而非编译期绑定静态方式传统 C// 编译期就固定了用哪个实现——换实现要改代码、重新编译MyAlgorithmImpl algo;consumer.use(algo);动态方式微服务// 提供方运行期某个时刻才把实现发布出去ctx-RegisterServiceIAlgorithm(newMyAlgorithmImpl);// 消费方运行期按接口名查询编译期完全不知道实现是谁autorefctx-GetServiceReferenceIAlgorithm();IAlgorithm*algoctx-GetService(ref);消费方二进制里没有任何对实现类的引用——链接时不需要实现库存在。2. 服务可以在任意时刻出现和消失这是动态最核心的含义。服务的生命周期不是程序启动时全部就绪、退出时全部销毁而是一个 .so 被加载 → 它的服务这时才注册进来US_INITIALIZE_MODULE静态初始化触发一个 .so 被卸载 → 它的服务自动注销ModulePrivate::RemoveModuleResources同一接口的服务可以中途被替换、增加、移除所以消费方必须面对的现实是“我要的服务现在可能有、可能没有、待会儿可能变”。这正是ServiceTracker存在的原因——它不是一次性查询而是持续跟踪// AddingService / RemovedService 回调应对服务的动态来去// QmitkAbstractView.cpp: L146 实际用例ctkServiceTrackermitk::IDataStorageService*m_DataStorageServiceTracker;3. 查询是按接口名字符串 属性过滤的晚绑定注册表内部就是一张接口名到实现列表的映射usServiceRegistry_p.h: L63-80// 接口名(字符串) → 按 ranking 排序的实现列表MapClassServices classServices;查找发生在调用那一刻Get返回当前 ranking 最高者而不是链接那一刻。配合 LDAP 过滤器还能按属性筛选GetServiceReferences(IAlgorithm,(typefast));同一接口多个实现共存、高 ranking 者胜出、随时可被更高 ranking 的新实现顶替——这些都是静态绑定做不到的。4. 事件驱动的感知每次注册/注销都会广播ServiceEventREGISTERED / 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_TYPEstd::string,Module*ModuleMap;// name → Module*US_GLOBAL_STATIC_WITH_DELETER(ModuleMap,modules,ModuleDeleter)每个模块有唯一自增 id、名字、磁盘路径ModuleInfo可以按名查询、全量枚举Module*mModuleRegistry::GetModule(MitkCore);// 按名找模块std::vectorModule*allModuleRegistry::GetModules();// 枚举所有模块2. 模块有显式的生命周期状态机普通 .so 只有加载/未加载两态且不可感知。模块系统给出完整生命周期且每次状态迁移都广播事件usModule.cpp: L119-171注册 → LOADING → (Activator::Load) → LOADED → UNLOADING → (Activator::Unload) → UNLOADEDActivator 钩子模块加载/卸载时有确定的回调时机ModuleActivator::Load/Unload初始化逻辑有了落脚点不再依赖脆弱的全局对象构造顺序ModuleEvent其他模块可监听谁被加载了/卸载了并做出反应3. 每个模块有自己的视角——ModuleContextModuleContextusModuleContext.cpp是模块与框架交互的专属句柄——注册服务、查服务、挂监听器都通过它。关键在于框架记得每笔操作是哪个模块做的// usModulePrivate.cpp: L118-146 —— 正因为记账到模块头上卸载时才能自动清理coreCtx-services.GetRegisteredByModule(this,srs);// 这个模块注册过什么coreCtx-services.GetUsedByModule(q,srs);// 这个模块用过什么这就是模块化与全局大杂烩的本质区别资源归属清晰卸载即自动回收不会留下悬空的服务和监听器。4. 模块自带元数据与资源manifest.jsonusModulePrivate.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.cppL39-63ModuleMapL75Register模块生命周期module/usModule.cppL119StartL173StopActivator 接口../include/usModuleActivator.hLoad/Unload模块上下文module/usModuleContext.cppL78RegisterServiceL98GetServiceReference按模块记账/清理module/usModulePrivate.cppL118-146RemoveModuleResourcesmanifest.json 解析module/usModulePrivate.cppL53-68模块资源系统module/usModuleResource.cpp/usModuleResourceContainer.cpp-服务注册表service/usServiceRegistry.cppL87RegisterServiceL128-133事件广播服务注册表结构service/usServiceRegistry_p.hL63-80两张映射表LDAP 过滤器module/usLDAPExpr.cpp-自动注册宏../include/usModuleInitialization.hL57-120US_INITIALIZE_MODULEMITK 实际用例Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cppL146ServiceTrackerMITK 实际用例Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cppL42FindResources