NingG +

Understanding the JVM:类加载及执行子系统的案例与实战

前言

这一章是给出了几个例子说明前面几章讲过的内容。我觉得关于tomcat那个例子对我来说太有用了。正好被这个问题困扰,然后今天看完瞬间感觉豁然开朗。而这一章关注点有:

因为平时跟字节码打交道的场景不多,而最近研究类加载器算是比较多。所以,这章我主要关注的就是类加载器相关的东西。

一、类加载器实例之——Tomcat

主流的Java Web服务器,如Tomcat、Jetty、WebLogic、WebSphere,都实现了自己定义的类加载器(一般都不止一个)。因为一个功能健全的Web服务器,要解决如下问题:

由于以上几个要求,如果只有一个CLASSPATH就很难实现了。所以各种Web服务器都会提供好几个CLASSPATH路径供用户存放第三方类库,这些路径一般都以lib或者classes命名。被放置在不同目录的类库,具备不同的访问范围和服务对象。通常而言,每一个目录都会有一个对应的自定义类加载器去加载设置在里面的Java类库。那么我们就以Tomcat为例来看一下。

1. Tomcat目录结构

在Tomcat目录结构中,有三组目录可以存放Java类库,另外加上应用程序自身的目录,一共四组:

  1. 放置在/common目录中:类库可被Tomcat和所有Web应用程序共同使用
  2. 放置在/server目录中:类库只能被Tomcat使用
  3. 放置在/shared目录中:类库可以Web应用程序共同使用,但是Tomcat不能使用
  4. 放置在/webapp/WEB-INF目录中:仅仅可以被这个Web应用程序使用,Tomcat和其它Web应用程序不能使用

为了支持这套目录,并对目录内的类库进行加载和隔离,Tomcat自定义了几个类加载器,这些类加载器按照经典的双亲委派模型来实现,如下图所示:

Note:上述 JVM 中的 App ClassLoader,是特殊的应用类加载器,也称为 Sys ClassLoader 系统类加载器

上述的类加载器:

其中WebAppClassLoader和JsperClassLoader会存在多个实例,每一个WebApp对应一个WebApp类加载器,每一个Jsp文件对应一个Jsp类加载器。

上面的图我们也能看出它们之间的隔离性:

2. Tomcat版本升级

对于Tomcat的6.x版本,只有指定了conf/catalina.properties配置文件中的server.loader和share.loader才会真正建立CatalinaClassLoader和SharedClassLoader的实例,否则会以CommonClassLoader代替。而默认的配置里面是没有设置这两个loader的,所以Tomcat 6.x顺理成章的把/commmon、/server、/shared这三个目录合并为一个/lib目录。这是Tomcat团队为了简化大多数部署场景所做的一项改进,如果默认设置不能满足需求,再通过修改配置完成3种类加载器的协同分工。

二、OSGi:灵活的类加载器结构

传说Java社区流传这样一句话:学习JEE规范,去看JBoss源码;学习类加载器,去看OSGi源码。可见,OSGi的类加载器机制确实值得学习。

OSGi(Open Service Gateway Initiative)是OSGi联盟制定的一个基于Java语言的动态模块化规范,最初是Sun、IBM、爱立信等公司联合发起,目的是使服务提供商通过住宅网关为各种家用智能设备提供各种服务,后来这个规范在Java其他技术领域都有不错的发展,现在已经成为Java世界中“事实上”的模块化标准。OSGi在程序员中最著名的应用案例Eclipse。你对Eclipse进行设置后,不需要重启就可以完成某个功能的开启或关闭,这就是OSGi的功劳。

看了之后,给我的感觉就是这东西非常灵活。OSGi的基本单位是Bundle,每个Bundle都可以有自己的类加载器和父加载器。而Bundle和类也差不多,内部也是package和class组成。但是一个Bundle可以声明它依赖的Java Packeage(通过Import-Package描述),也可以声明它允许导出发布的Java Package(通过Export-Package描述)。在OSGi中,Bundle之间的依赖关系从传统的上层模块依赖叠层模块转变为平级模块之间的依赖,而且类库的可见性得到了非常精确的控制,一个模块里只有被Export过的Package才能被外界访问,其他的Package和class会被隐藏。所以,OSGi才能支持热插拔技术。

而OSGi拥有这么诱人特性的原因就是它的类加载器架构。举一个简单的例子:

那么,三者之间的关系可以用下图表示:

从上图可以看出来,OSGi里面的加载器不再是双亲委派模型的树形结构,而是进一步发展成了一种运行时才能确定的网状结构。更灵活的同时肯定带来了更复杂的使用方法,这点在实际应用中确实值得权衡。

一般来说,在OSGi中,加载一个类可能发生的查找行为和委派关系会比上图复杂的多,大体规则可以总结为:

Top