osgi通过bundle更新与服务重注册实现动态逻辑替换,而非直接修改内存变量;依赖服务生命周期、类加载隔离及servicefactory机制,在不重启jvm前提下完成毫秒级热替换。

OSGi 本身不直接支持“动态替换内存中的对象变量”,它提供的是模块级(Bundle)的热部署能力,即在运行时安装、启动、停止、更新、卸载 Bundle,从而实现功能模块的动态加载与切换。所谓“替换内存中的对象变量”,需理解为:通过 Bundle 更新或服务重注册,使新版本逻辑接管旧实例,间接达成运行时状态/行为的切换。这不是修改某个 static 变量或堆中对象字段,而是依托 OSGi 的服务生命周期与类加载隔离机制完成的。
以下从三个关键角度说明如何在实战中达成这一目标:
如何用 Bundle 更新触发逻辑替换
Bundle 更新(update())会触发框架重新解析依赖、重建类加载器,并在 ACTIVE 状态重启其 Activator 和已注册服务。这意味着:
- 原 Bundle 中所有单例对象(如
Activator实例、ServiceFactory创建的服务)会被销毁 - 新 Bundle 的类被新
ClassLoader加载,新Activator执行,新服务实例注册进服务注册表 - 消费方通过
ServiceTracker或@Reference获取的服务引用会自动切换到新实例(取决于服务策略)
示例流程:
- 旧 Bundle 提供
CalculatorService,导出com.example.Calculator接口 - 客户端 Bundle 通过
BundleContext.getServiceReference()获取并缓存该服务 - 执行
oldBundle.update(newJarInputStream) - OSGi 框架停止旧 Bundle → 注销其服务 → 加载新 Bundle → 启动 → 注册新版
CalculatorService - 若客户端使用动态服务引用(如
ServiceTracker的addingService()回调),即可无缝获取新实例
注意:若客户端强引用旧服务对象(如
static CalculatorService cached),不会自动更新——必须通过服务查找机制按需获取,而非自行缓存实例。
如何用服务工厂(ServiceFactory)控制实例粒度
当需要每个消费者获得独立实例(例如带用户上下文的状态对象),应使用 ServiceFactory:
public class UserConfigFactory implements ServiceFactory<userconfigservice> {
@Override
public UserConfigService getService(Bundle bundle, ServiceRegistration<userconfigservice> reg) {
return new UserConfigService(bundle.getBundleId()); // 每 Bundle 一个实例
}
@Override
public void ungetService(Bundle bundle, ServiceRegistration<userconfigservice> reg, UserConfigService service) {
service.close();
}
}</userconfigservice></userconfigservice></userconfigservice>
注册时声明:
context.registerService(UserConfigService.class, new UserConfigFactory(),
Map.of(Constants.SERVICE_SCOPE, Constants.SERVICE_SCOPE_BUNDLE));
这样,Bundle 更新后,新 Bundle 调用 getService() 就会创建全新实例,旧实例随 Bundle 停止而释放。
如何配合 Web 控制台快速验证热替换效果
本地开发 AEM 或 Felix 环境时,可利用 Web 控制台(/system/console/bundles)手动操作:
- 上传新版本 Bundle JAR(同 SymbolicName + 更高 Version)
- 点击旧 Bundle 的 Update 按钮(非 Install)
- 观察日志:
STOPPED → INSTALLED → RESOLVED → STARTING → ACTIVE - 切换到
/system/console/services,确认服务 PID 对应的新版本 Bundle ID 已注册 - 若服务含
@Property(name = "service.ranking", intValue = 100),还可利用排名控制优先级,实现灰度切换
无需重启 JVM,整个过程毫秒级完成,内存中旧类、旧服务对象由 GC 自动回收(前提是无外部强引用)。
本质上,OSGi 的热部署不是“改变量”,而是“换组件”。只要遵循服务契约、避免静态全局状态、使用动态服务查找,就能安全实现运行时逻辑与状态的平滑替换。










