《How Tomcat Works》通译(6) 之 类加载器

《How Tomcat Works》翻译(6) 之 类加载器

第八章 、Loader加载器

一、前言部分

 

在前面的章节你已经看到了一个简单的loader实现,这是使用了加载servlet类。这章主要就解释了在Catalina中一个标准的web应用程序Loader组件。一个servlet容器需要定制Loader,而不是仅仅使用system's类加载器,因为这样的实现机制会导致一些不受信任的servelt被运行。如果你想加载全部servlets,需要引用servlet类的其他类就是使用了系统类加载器加载。正如我们前面章节所看到那样,一个servlet将能够访问任何类和类库如:运行在java虚拟机的环境变量CLASSPATH,这样就违背类安全的原则。因为servlet仅仅允许在一个限制的目录下加载class如:WEB-INF/classes目录以及子目录,还有部署的类库WEB-INF/lib类库目录。这就是为什么servlet容器需要一个自己的类加载器。在一个servlet容器中的每个web应用程序(Context)有自己的类加载器。一个Loader加载器加载类是运用了某些规则。在Catalina中一个Loader是由org.apache.catalina.Loader接口代表。

 

为什么需要Tomcat需要自己的类加载器的另外一个原因就是:当一个类在WEB-INF/classes 或者在WEB-INF/lib目录下发生改变,那么就需要自动加载该修改的类(也叫热部署)。The class loader在Tomcat中加载器的实现是使用了一个独立的线程:一直核实加载的servlet的时间标记和支持的类文件。为了支持热部署,一个类加载器一定要实现org.apache.catalina.loader.Reloader接口。

 

 首先本章的第一部分主要是回顾一个来加载器的机制原理。然后,就会说明the Loader接口(全部的类加载器一定要实现它),又会说明the Reloader接口。看完类加载器的实现和loader类,这章就会将一个应用目的是想说明在Tomcat中怎样使用类加载器。

 

  另外本章还拓展了两个知识点:repository和resources。A repository是一个类加载器搜索class的存储位置,而resources在类加载器指向一个DirContext对象,下面不知道怎么翻译了还是贴英文吧(whose document base points to the context's document base)

 

二、Java 类加载器

 

只要你创建一个java 类实例,这个类就会被加载到内存中。The JVM使用类加载器加载一个class。类加载器一般搜索java核心类库和Java CLASSPATH环境变量下的目录。如果你没有找到一个具体的class,那么虚拟机就会抛出java.lang.ClassNotFoundException异常。

 

   总从J2SE1.2开始,JVM自己里面拥有三个类加载器:bootstrap class loader,exension 类加载器,system加载器。这三个类加载器是有相互等级关系的,即父与子的关系,因此bootstrap类加载器是最高等级而the system加载器等级最低。

 

 The bootstrap加载器使用了JVM中的bootstrap。无论你什么时候调用the java.exe程序,该加载器都会工作。诸如这样,bootstrap类加载器使用原生代码实现(可能是用c语言实现的),因为经常需要加载JVM运行中所需要的类。同时它也负责加载java classses的核心类,比如java.lang和java.io包。它也搜索核心类库如:rt.jar,il8n.jar等得,搜索哪个类库取决于JVM的版本和操作系统。

 

扩展类加载器(The extension class loader)主要是负责在标准扩展目录下加载classes.这样做的目的的是使得程序设计者方便,因为只需要复制JAR文件到扩展目录下就可以,the jar 文件会自动搜索。扩展目录是根据开发厂家不同而目录也不一样。Sun's JVM's标准的扩展目录是/jdk/jre/lib/ext.

 

The system加载器是一个默认的加载器,主要是搜索JVM中的CLASSPATH设置的环境变量里面的.class和JAR文件。

因此你会有一个疑问JVM是怎样使用类加载器的呢?答案是设计者使用了委托模型,这样做的原因主要为了安全考虑。注解下面是我补充的一个委托模型图,主要是为了大家方便理解:

 ----------------------------------------------------------  

在JVM中,每个类都是由一个java.lang.ClassLoader的派生类的实例执行装载工作的。这个ClassLoader类位于java.lang包中,因此编程人员能够非常方便的对它进行扩展,以实现那些对类进行装载过程中的特定功能需求。
java有两种类型的classloader,一种是user-defined的,一种是jvm内置的bootstrap class loader,所有user-defined的class loader都是java.lang.ClassLoader的子类. 而jvm内置的class loader有3种,分别是 Bootstrap ClassLoader, Extension ClassLoader(即ExtClassLoader),System ClassLoader(即AppClassLoader).

 

获取Classloader加载顺序


《How Tomcat Works》通译(6)  之 类加载器

运行的结果:sun.misc.Launcher$AppClassLoader
         sun.misc.Launcher$ExtClassLoader

 

自定义Classloader


《How Tomcat Works》通译(6)  之 类加载器
 
《How Tomcat Works》通译(6)  之 类加载器


《How Tomcat Works》通译(6)  之 类加载器

-----------------------------------------------------------

注解:上面的图解主要为了解释委托模式是怎么回事,这个图也是参考了别个的呵呵呵。。。

一旦一个类需要被加载,the system加载器首先被调用。然而,该加载器不会马上加载class,相反而是委派给父类加载器(extension 类加载器)。The extension类加载器同时也委托给父亲加载器(bootstrap加载器)。因此the bootstrap加载器一直是首要的机会加载一个class。如果the bootstrap加载器没有找到所需要的class时,那么the extension将会加载class,如果还没有找到,the system 类加载器执行该任务。如果执行该任务失败了,那么就会抛出java.lang.ClassNotFoundException异常。那么有的人会问为什么要这样来回的调用啊??

 

答案就是利用委托模型这样做的话保证了安全。正如你所知道的,你能够使用安全管理来限制访问某一个确定的目录。现在举一个列子来说明原因:当黑客要写一个java.lang.Object类(该类能够在硬盘上任何目录下能访问),因为JVM确信java.lang.Object类,然而JVM不会关注是否有威胁,结果导致被定制的java.lang.Object被加载了,因此安全管理将会很容器瘫痪。呵呵,幸运的是这样的结果不会发生,因为有了委派模型他会第一时间的在the startstrap加载器加载该类。这儿是JVM是怎样工作的。

 

当定制的 java.lang.Object类在程序的某些地方被调用,the system类加载器委派给the extension类加载器,然而他有委托给the bootstrap类加载器。The bootstrap类加载器搜索核心库然而就找到了标准的java.lang.Object,把它实例化。结果就是自己定制的java.lang.Object从来没有加载过。

 

在java中关于类加载器机制的一件很好玩的事就是:你能够写你自己的类加载器(通过扩展抽象类 java.lang.ClassLoader).Tomcat为什么需要自己定制类加载器的原因有以下几点:

 

  1、在加载类时要服从具体的某些规则

  2、存储先前已经加载的类(也叫缓存)

  3、预先加载Classes,这样提高系统效率

 

1 楼 kangsg219 2010-12-07  
very good , keep going!