OSGi之所以能够实现模块热插拔和模块内部可见性的精准控制都归结于其特殊的类加载机制.加载器之间的关系不再是双亲委派模型的树状结构,而是发展成复杂的网状结构。
根据OSGi规范说明:
对于类或资源加载,框架必须遵循以下规则。 当请求bundle的类加载器加载类或资源时,必须按以下顺序执行搜索:
- 如果类或资源在java.*包中,则将请求委托给父类加载器; 否则,继续下一步搜索。 如果请求被委托给父类加载器还找不到类或资源,则搜索终止并且失败。
- 如果类或资源来自引导委派列表(系统变量org.osgi.framework.bootdelegation)中包含的包,则将请求委托给父类加载器。如果在那里找不到类或资源,继续下一步搜索。
- 如果类或资源属于声明在Import-Package导入的包中,或者是在先前的加载中动态导入的,那么请求将委托给声明Export-Package这个包的bundle的类加载器;否则继续下一步搜索。如果请求被委托给导出类加载器但找不到类或资源,则失败。
- 如果类或资源位于在多个Require-Bundle包中导入的包中,则请求将按照清单中指定的顺序委派给其他包的类加载器。这个过程中使用深度优先策略;如果未找到类或资源,则继续下一步搜索。
- 搜索bundle的内嵌jar的类路径(Bundle Class Path)。如果找不到类或资源,继续下一步。
- 查找Bundle的Fragment Bundle中导入的包, 如果没找到继续下一步
- 如果类或资源位于自己导出的包中,则搜索结束并失败。
- 否则,如果类或资源位于DynamicImport-Package导入的包中,则尝试动态导入包。
- 如果动态导入包成功,则将请求委托给导出这个包的类加载器。如果请求被委托给导出类加载器并且找不到类或资源,则搜索终止且失败。
总体图:
但这种模式也会产生许多隐患,比如循环依赖问题,如果BundleA依赖BundleB , BundleB依赖BundleC, BundleC又依赖BundleA, 这可能在加载Bundle的时候导致死锁问题。为了避免这种情况,根据OSGi规范说明,在这种情况下,框架必须在第一次访问Bundle的时候做标记,不去访问已经访问过的Bundle.
另外,在OSGi中Bundle都有自己独有的ClassLoader
, Fragment Bundle不同于普通Bundle, 其和其附着的Host Bundle共享一个ClassLoader.
如果在开发Liferay的过程中遇到Unresolved import-Package问题,可以参考我在Liferay的一篇博客
参考文献
-
深入理解Java虚拟机第三版(周志明)