osgi类加载抛弃双亲委派,采用“先本bundle、再依赖、最后父加载器”策略,通过import-package与export-package显式声明依赖,构建受控有向依赖图,实现包级隔离、多版本共存与热插拔。

OSGi 的类加载不是“向上委托”,而是“按需导出+显式导入”
传统双亲委派是单向、线性的责任链:App → Ext → Bootstrap,一层层往上甩请求,失败才回落。OSGi 完全抛弃这套逻辑——它不设统一父加载器,每个 Bundle(模块)自带独立的类加载器,彼此之间没有父子关系,而是靠元数据声明依赖:一个 Bundle 必须在 MANIFEST.MF 中用 Import-Package 明确写出要使用哪些包,再由 OSGi 框架在运行时动态解析哪个 Bundle 实际导出了(Export-Package)这些包,然后建立跨 Bundle 的类可见性链接。
类查找走的是“服务注册表+包空间映射”,不是递归调用 loadClass()
当 Bundle A 加载 com.example.Service 时,它的类加载器不会调 parent.loadClass(),而是直接查 OSGi 的内部包注册表:
- 先检查本 Bundle 的私有类路径(Bundle-ClassPath)是否包含该类
- 若无,则查本 Bundle 的
Import-Package列表,定位到导出该包的 Bundle B - 再委托 Bundle B 的类加载器去加载(注意:这是协作,不是继承关系中的委托)
- 如果多个 Bundle 都导出同一包,OSGi 按版本、符号名等规则选一个主提供者,其余被忽略
网状结构的核心支撑是“类空间隔离”和“动态可插拔”
每个 Bundle 有自己独立的命名空间,即使两个 Bundle 都加载了 org.slf4j.Logger,只要它们来自不同 Bundle,JVM 就认为这是两个不同的类(哪怕字节码完全一样)。这种隔离让模块可以:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 共存不同版本的相同类库(如 Bundle X 用 guava 28,Bundle Y 用 guava 32)
- 热部署/卸载某个 Bundle,不影响其他 Bundle 的类加载状态
- 实现真正的依赖闭环:A 导入 B 的包,B 可以反过来导入 A 的另一个包(双 imports),形成环形依赖——这在双亲委派下会直接死锁
打破双亲委派不是“绕过”,而是“重定义加载契约”
OSGi 不是简单地重写 loadClass() 来跳过父加载器;它是从架构层面重构了类加载的语义:
- 加载动作仍调用
loadClass(),但该方法内部逻辑已替换为 OSGi 的解析引擎 - 不再依赖“类加载器层级”,而依赖“Bundle 生命周期状态”(Resolved / Active / Installed)
- 类能否加载成功,取决于包导入导出是否匹配、版本约束是否满足、目标 Bundle 是否处于可提供状态
所以 OSGi 的“网状”本质是运行时根据模块契约动态编织的类可达图,不是静态继承链的变形。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










