OSGi 通过独立 BundleClassLoader 和显式依赖声明实现类隔离:每个 Bundle 拥有专属类加载器,无父子继承关系,仅按 Export/Import-Package 声明加载可见类,同名类因加载器不同而互不兼容,卸载时类加载器及所加载类可被彻底回收。

OSGi 通过为每个 Bundle 分配独立的类加载器,并用显式依赖声明替代双亲委派,实现真正意义上的类隔离。
每个 Bundle 拥有专属 BundleClassLoader
OSGi 不复用 JVM 默认的父子层级结构,而是为每个已解析的 Bundle 创建一个独立的 BundleClassLoader 实例。这个加载器不继承自其他 Bundle 的加载器,彼此之间没有父子关系,仅靠运行时契约协作。它负责加载本 Bundle 的 Bundle-ClassPath 下的类和资源,且只对自身导出(Export-Package)的包对外可见。
加载顺序严格遵循“本地优先、依赖显式、父加载器慎用”
当某个 Bundle 需要加载一个类时,其 ClassLoader 执行以下查找链:
- 先检查该类是否在本 Bundle 的 Export-Package 列表中,且字节码存在于本地路径;
- 若不在,再查本 Bundle 的 Import-Package 声明——框架据此定位到提供该包的另一个 Bundle,并委托其 ClassLoader 加载;
- 仅对 java.*、javax.*(核心部分) 等系统包,才委派给 Bootstrap 或 Framework ClassLoader;
- 未声明导入的包,哪怕物理存在,也会直接抛出 ClassNotFoundException。
同名类在不同 Bundle 中是完全不同的类型
JVM 判定类唯一性的依据是“类全限定名 + 加载它的 ClassLoader 实例”。因此:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
- Bundle A 和 Bundle B 都导入 org.slf4j.Logger,但分别来自不同导出方,它们加载的 Logger 是两个互不兼容的 Class 对象;
- instanceof、强制转型、静态字段共享等操作都会失败;
- 这种隔离天然支持多版本共存,比如一个 Bundle 用 guava 28,另一个用 guava 32,互不影响。
隔离依赖 Manifest 元数据驱动,不是路径或命名约定
所有可见性边界由 META-INF/MANIFEST.MF 中的声明控制:
- Export-Package: com.example.api;version="1.2.0" —— 明确导出哪些包、带精确版本;
- Import-Package: com.example.api;version="[1.0,2.0)" —— 表达能力需求,框架在启动时做符号解析,匹配满足约束的 Exporter;
- 没有导出的包,即使打包进 JAR,也无法被其他 Bundle 访问;
- 缺失或版本不匹配的导入,在解析阶段(Resolved 状态前)就报错,而非运行时随机失败。
卸载即释放:类加载器生命周期与 Bundle 状态绑定
隔离不只是加载时的控制,更延伸到卸载环节:
- 当 Bundle 进入 UNINSTALLED 状态,其 BundleClassLoader 被框架显式丢弃;
- 该加载器加载的所有类对象失去强引用,可被 GC 回收;
- 只要没有外部静态引用、线程局部变量或服务注册残留,实例也会一并清理;
- 新版本 Bundle 安装后启用全新 ClassLoader,从零开始加载,无旧状态污染。

















