论坛首页 Java企业应用论坛

JIOPi类加载模型-类库分离式部署和隔离运行的实现原理

浏览 2024 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-04-30   最后修改:2010-04-30

OSGi的类加载模型相似,多个JIOPi模块运行在同一个JVM之内,但互相并不可见。JIOPi使用与OSGi相似但不完全相同的类加载规则以保证模块间既可以相互隐藏模块的具体实现,又可以通过接口相互使用。

 

JIOPi v0.1是为兼容纯POJO设计的,可以直接将POJO类库放入JIOPi模块库而无需做任何改动,因此,在JIOPi v0.1的类加载模型中,并没有为模块间的相互依赖提供很好的支持,除非是在特别有意的部署方式下,或使用JIOPi提供的反射机制访问,否则JIOPi模块间的相互访问是比较困难的。

 

JIOPi的名字(Java Interface Oriented Programming initiative )已经指出,JIOPi模块的服务导出是借助接口来完成的。当然,如果使用JIOPi提供的反射机制进行模块访问,那么就无所谓服务的导出了,模块间也是可以任意互相访问的,在这种模式下,服务的导出是借助JIOPi提供的API来完成的。

 

所以,为了不使用JIOPi的接口使用反射机制来访问模块,一个模块至少应该由两个Jar包组成:一个为导出的服务接口APIJar包,该Jar一般情况下应只包含接口类(Interface),而不包含实现类(Class),以后简称为API.jar另一个是对模块接口API的实现类,以后简称为IMPL.jar

 

为了描述JIOPi模块的类加载机制,我们先定义两个名词:

系统层,即由ContextClassLoader及其父ClassLoader加载的类,当前应用的类程序也就是被加载在系统层的,自然JIOPiAPI也存在于该层。对于应用而言,该层的类都必须是唯一的,不可能存在同一类的多个版本。

模块层,由JIOPi容器创建的ClassLoader加载的类,不同模块的不同版本的类将被加载在不同的模块层。

 

ClassLoader机制不难理解,系统层的类是不能直接访问到模块层的类的,所以对模块的访问需要借助JIOPiAPI

那么模块层的类是否可以访问系统层的呢?

 

 

JIOPi对模块层的类加载优先级是这样定义的:

1.       Bootstrap加载(如java.*的类)

2.       JIOPiAPI(org.jiopi.framework.*)必须由系统层加载

3.       从当前模块包含的Jar包中加载

4.    从系统层加载

 

 

也就是说模块层的类可以访问系统层的类,在要访问的类没有被包含在该模块的Jar包中时,也正因为如此,模块才可以借助部署在系统层的接口,将对当前模块对象的访问暴漏给系统层的类。

 

然而由于当前模块Jar包优先加载规则,如果模块中已经部署了API.jar,那么对该模块的访问便只能通过JIOPiAPI了。而如果不在模块中部署API.jar,而系统层也没有部署,那么该模块在类加载时便会出现接口类无法找到的问题。这就导致如果模块中不包含API.jar,那么就必须在系统层部署,否则模块无法运行;而如果部署了,那就只能通过JIOPiAPI来访问了。

 

这个冲突是可以借助动态代理类来解决的,即永远在模块中部署API.jar,当系统层也部署了API.jar时,使用动态代理类来解决由不同ClassLoader加载的同名类赋值时产生的强制类型转换异常。

 

考虑到动态代理类存在性能问题,并且在草案中的JIOPi已经制定了解决该冲突的编程风格,所以没有将自动动态代理列入规范之中。

 

因此,在JIOPi v0.1(或者说直接将POJO放入JIOPi模块库的模式中),对于一个模块,你只能在 使用接口访问 JIOPi模式访问 之中二选一,或者将一个模块发布成两个,一个包含API.jar而另一个则不包含,这也就是为什么HelloWorld演示程序中会出现 jiopi.ibean.helloworld.m jiopi.ibean.helloworld.i 两个模块的原因了。

 

基于上述的模块类加载原则,你有以下几种方式控制 系统层与模块、模块与模块 的访问方式。

 

全部使用JIOPiAPI

在这种模式下,各个模块程序完全运行在自己的ClassLoader中,无论是系统层访问模块,还是模块之间的互相访问,均借助JIOPi来进行,这种方式的优点是使用最为简单的,并且为使用各种JIOPi模块,项目的lib中只需部署一个JIOPi实现容器的Jar即可,各个模块之间也可直接借助JIOPiAPI访问其他模块。但缺陷是无法得到编译器和IDE的支持,写出的代码出错率会比较高,而且代码可读性较差,由于使用反射机制调用,性能也会有所影响。

 

全部模块只使用IMPL.jar部署

在这种模式下,必须将系统层直接或间接使用到的模块所需的API.jar均部署在系统层,为了避免代码中出现JIOPiAPI代码,应当使用其他IoC容器,如Spring来完成系统层对所需各个模块的依赖关系的注入配置。

 

关于ContextClassLoader的特殊规则

由于ContextClassLoader(Thread.currentThread().getContextClassLoader())的特殊性,特别是一些类库程序在初始化时会从ContextClassLoader中获取类,如大多数xml解析程序,log4j等,如果在模块中部署了这些类库,就会因为模块类加载器的非Java标准的类加载次序规则而出现问题,因此JIOPi规定在类初始化阶段(从类的加载到对象实例的生成过程)JIOPi容器应当将ContextClassLoader设置为当前模块的类加载器,并在加载完毕后设置回原ContextClassLoader,以保证在一些模块的初始化阶段直接从ContextClassLoader中获取类时能够被正确执行。因此如果你的模块直接或间接(如使用xml)的从ContextClassLoader获取对象,那么你应当保证在类或类对象的初始化阶段完成该类语句的执行。

论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics