类装载器
Java中的Class实例,不仅是全限定名(包名+类名)的函数,也是类装载器的函数,即::Class = f(name, Li, Ld)
其中,name表示类名,Li为初始装载器,Ld表示定义装载器。
初 始装载器为在其上发生loadClass调用返回Class实例的那个装载器;定义装载器则是实际为Class实例执行defineClass,从 bytecode中读入类定义的那个装载器。因为loadClass方法可以被子类重载,其中对defineClass的调用有可能被delegate给 其它装载器做,所以Li和Ld不一定相同。
于是,
- 两个不同类装载器装载的类,即使类全限定名相同,都不能相互cast,否则抛出ClassCastException。
- 如果装载器L1装载的类p.A有包可见的方法f,则装载器L2装载的类p.B不能访问f,否则抛出IllegalAccessException。
- 如果有Class1和Class2的name,Li相同,而Ld不同,它们实例之间的引用赋值时会触发装载限制检查,抛出LinkageError。
为了热deploy模块的需要,JBoss实现了自己的类装载器U nifiedClassLoader3,一般来说,一个顶层的deployment就有一个UnifiedClassLoader3实例为之工作。JBoss所装载的类呈平面模型——也就是说,一个deployment所装载的类,其他 deployment是可见的。全局唯一的UnifiedLoaderRepository3实例用于管理这 些类,以及装载它们的UnifiedClassLoader3。UnifiedLoaderRepository3 实例和UnifiedClassLoader3实例是一对多的关系。
一个deployment要装载一个类,
1)先查看全局的 UnifiedLoaderRepository3实例里的cac he是否已存在,存在则返回。
2)否则,用deployment自己的UnifiedClassLoader3装载,成功则更新 UnifiedLoaderRepository3的cache ,并返回。
3)否则用UnifiedLoaderRepository3里的其他 UnifiedClassLoader3装载,成功 则更新 UnifiedLoaderRepository3的cache ,并返回。
4)否则抛出ClassNotFoundException。
要查看默认的全局唯一的UnifiedLoaderRepository3里装载Class的情况,见名为JMImplementation:name=Default,service=LoaderRepository的MBean。它有 displayClassInfo(String)的方法,用于查看有关Class的ClassLoader;还有 getPackageClassLoaders(String)方法,用于得到“哪些ClassLoader有装载某个package的能力”。
JBoss的平面类装载机制决定了,一般情况下,相同类名的类在整个JBoss运行环境中,只装载一次。如果要不同的EAR包中,有不同版本的a.b.C类,需要为EAR指定自己的 HeirarchicalLoaderRepository3 ,先和这个Repository一起工作找到自己版本的C类而不是到全局的UnifiedLoaderRepository3里去找全局的版本。这个机制叫做Scoping Classes——即EAR有自己独立的类装载空间。要配置Scoping Class,需要在META-INF/jboss-app.xml 描述符里注明:
<jboss-app>
<loader-repository>some.dot.com:loader=webtest.ear
</loader-repository>
</jboss-app>
其中,MBean名称(粗体部分)可以是符合MBean名称规范的随意名称。
说明:
1)Server Loader是一个NoAnnotationURLClassLoader的实例(见ServerLoad.load方法),它控制的URL有$ JBOSS_HOME/lib下的jboss-jmx.jar、concurrent.jar、log4j-boot.jar、jboss- common.jar、jboss-system.jar、jboss-xml-binding.jar、namespace.jar以及其他用命令行参 数指定的jar包。它负责load的类包括org.jboss.system.server.ServerImpl、 org.jboss.mx.server.MBeanServerImpl、UnifiedLoaderRepository3等等。
2)全局唯 一的UnifiedLoaderRepository3实例是实现JBoss平面类装载模型的关键。这个实例是标准的 javax.management.MBeanServer的成员,可以用标准的方法: MBeanServer.getClassLoaderRepository()得到。
3)ServerTCL是一个 UnifiedClassLoader3的实例,它控制的URL是server/<config>/conf(用于有关resources的 装载)。它负责ServerInfo、ServiceController、MainDeployer、JARDeployer、SARDeployer 等MBean的类装载和实例化,在这几个MBean的创建和start的上下文中,它作为当前线程ContextClassLoader。
实现自定义service
JBoss平台没有在它之上实现service规定了诸多限制,相反,它为MBean的部署和访问提供了许多方便:Service可以在不重启动 JBoss平台的情况下,被热部署或者热删除;Service不必担心它引用的jar包如何到位,将这些jar包放在合理的位置,它们就直接能被部署器找 到了;如果service需要较好的生命周期管理,或引用其他service,对JBoss平台的依赖才派上用场。
一个部署到JBoss的service可以是放置在$JBOSS_HOME/server/<config>/deploy目录下的一个 *.sar包,一个*.sar文件夹,或者由*-service.xml描述,或者是其他的形式。一个service经由xml文件描述可以暴露多个 mbean。
一个部署到JBoss的service可以是不依赖于JBoss平台的任何符合JMX规范的mbean,也可以是一个借用JBoss Service的生命周期管理和依赖管理的一个MBean,也可以是利用JBoss XMBean描述规范将普通的Java对象“升级”而出的一个mbean:
1)mbean可以没有对JBoss的任何依赖。以standard mbean为例,三个文件就足够提供一个service:org.abc.HelloMBean接口,实现该接口的org.abc.Hello类,以及一 个service描述文件——该描述文件可以是sar包或目录内的META-INF/jboss-service.xml,也可以是一个单独作为部署单位 的文件,如直接在deploy目录(或其子目录)下的hello-service.xml。
2)如果用户定义的mbean需要生命周期的管理,它 可以实现org.jboss.system.ServiceMBean接口及其create,start,stop,destroy等生命周期方法,或者 干脆扩展org.jboss.system.ServiceMBeanSupport类。
3)开发者可以在service描述文件里说明本 mbean对其他mbean的依赖。所谓“依赖”是指,本mbean在create/start时先检查被依赖的mbean是否已经存在/启动,否则调用 它的create/start方法;一个被依赖的mbean被stop/destroy时,依赖于它的mbean首先被调用stop/destroy方 法。
4)除了基于JMX标准的mbean实现方案,JBoss还支持用一个xml文件描述的方式将普通的Java对象开放为一个mbean,即 XMBean。在service描述文件里可以用xmbean-dd属性指明一个外部的XMBean定义,或者将xmbean-dd属性赋值为"",在 mbean标签中内嵌xmbean的内容说明。XMBean的优势在于a)不需要目标Java对象实现任何JMX有关的接口;b)相对Standard MBean可以在xmbean-dd的xml内提供丰富的元数据描述信息。
理论上,放置在$JBOSS_HOME/server/<config> /deploy(包括子目录)下的所有jar包都会被URLDeploymentScanner监控,为其他的service所用。这些jar包也是可动 态添加的。实际上,我们吧my-service.xml和my-service.jar(假设my-service.xml描述的mbean的类定义在 my-service.jar内)放置在deploy目录下,然后启动JBoss,my-service是不能成功启动的,因为JBoss的 MainDeployer部署包有一个顺序,基本上是.deployer>deployer.xml>.sar>.rar> ds.xml>service.xml>.har>.jar>.war>.wsr>.ear>.zip>. bsh>.last,可见my-service.xml是在my-service.jar之前得到部署的,自然引用不到my- service.jar之内的类了。
如果将jar放在sar(目录或包)内的任一层子目录内,sar对应的service引用这些jar是没有问题的,因为sar相关的部署器做了“深度优先”的部署——先部署这些jar包,然后部署sar。
因为UnifiedLoaderRepository所决定的平面类装载模型,在一个sar内引用(部署)成功了a.jar,其他大部分service也可以使用a.jar了。
*-service.xml里的server标签有一个classpath的子标签,用来指定sar的外部类路径。classpath标签的 codebase属性默认是以$JBOSS_HOME/server/<config>为根的,archives属性的值可以是一个或多个逗 号格开的jar文件名(注意只能是一层文件名)