1.Tomcat:正统的类加载器架构
2.OSGi:灵活的类加载器架构
OSGi中的每个模块(成为Bundle)与普通的Java类库区别并不太大,两者一般都以JAR格式进行封装,并且内部存储的都是Java Package和Class。但是一个Bundle可以声明它所依赖的Java Package(通过Import-Package描述),也可以声明它允许导出发布的Java Package(通过Export-Package描述)。在OSGi里面,Bundle之间的依赖关系从传统的上层模块依赖底层模块转变为平级模块之间的依赖,而且类库的可见性能得到非常精确地控制,一个模块里只有被Export过的package才可能被外界访问,其他的Pacakge和Class将会被隐藏起来。除了更精确地模块划分和可见性控制外,引入OSGi的另外一个重要理由是,基于OSGi的程序可以实现模块级的热插拔功能,当程序升级更新或调试出错时,可以只停用、重新安装后启动程序的其中的一部分,这对企业级程序开发来说是一个非常有诱惑以的特性。
OSGi之所以能有上述诱人的特点,要归功于它灵活的类加载器架构。OSGi Bundle类加载器之间只有规则,没有固定的委派关系。例如,某个Bundle声明了一个它依赖的Package,如果有其它Bundle声明发布了这个package后,那么对这个package的所有类加载动作都会委派给它发布的Bundle类加载器去完成。不涉及某个具体的Package时,各个Bundle加载器都是平级的关系,只有具体使用到某个Package和Class的时候,才会根据Package导入导出定义来构造Bundle间的委派和依赖。
另外,一个Bundle类加载器为其它Bundle提供服务时,会根据Export-Package列表严格控制访问范围。如果一个类存在Bundle的类库中但是没有被Export,那么这个Bundle的类加载器能找到这个类,但不会提供给其它Bundle使用,而且OSGi平台也不会把其它Bundle的类加载请求分配给这个Bundle来处理。
在OSGi里,加载一个类可能发生的查找行为和委派关系会比上图中显示的复杂得多,类加载时可能进行的查找规则如下:
- 将以java.*开头的类,委派给父类加载器加载。
- 否则,将委派列表名单内的类,委派给父类加载器加载。
- 否则,将import列表中的类,委派给Export这个类的Bundle的类加载器加载。
- 否则,查找当前Bundle的ClassPath,使用自己的类加载器加载。
- 否则,查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。
- 否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。
- 否则,类查找失败。
《深入理解Java虚拟机》读书笔记系列现共有7篇文章,如下。如需了解更详细内容,可购买原书。