Java类加载器学习札记

Java类加载器学习笔记
  刚在commons-logging的网站上看到了一篇讲ClassLoader的文章,很有必要做个笔记,以防以后忘记了。

  一、Class是怎么被加载的?
  每个Class对象都保留着加载自己的类加载器的引用,可以通过Class对象的getClassLoader方法来获得其引用。ClassLoader通过loadClass方法来加载这个类。
  一般来说,loadClass方法的实现逻辑是这样的:
  1.该类如果被加载过,则直接返回以前加载过的Class对象;否则继续下一步;
  2.调用findClass把类名对应的字节码文件解析成字节数组。
  3.调用defineClass将字节数组解析成为class对象,该方法是native方法,完成了解析字节码文件的工作。
  由此看来,如果我们想实现自己的类加载器(比如,通过网络加载远程的字节码文件),那么就需要重写findClass方法来完成。

  二、类加载器的类型
  Java的系统类加载器有三种类型:
  1.bootstrap classloader:用于加载核心类库(java.*)
  2.extension classloader:用于加载扩展类库(javax.*)
  3.system classloader:用于加载我们自己写的类,当然也包括我们用到的类库。

  三、Parent-First & Child-First
  每个ClassLoader(? extends ClassLoader)都有它的父加载器,可以通过ClassLoader的getParent方法获得。可以看的出来,树的根就是bootstrap classloader。当我们要自定义一个类加载器的时候,首先需要考虑的问题就是先让父类加载器去加载还是先自己加载,所以就有了Parent-First和Child-First。ClassLoader的默认实现就是Parent-First,这也是最常见的实现方式。

  四、ContextClassLoader
  通常来说我们不会实现自己的ClassLoader,但这并不代表我们没有用到过自定义的ClassLoader。commons-logging里的这篇文章提供了一个非常好的例子。
  一般来说Web容器会开启一个线程池来接受处理Http请求,找到处理该请求的Web app,然后交给它处理。Web容器里是可以跑多个应用的,那么,想象一下,这多个应用都是通过同一个类加载器(也就是加载Web容器的类加载器)加载的吗?显然不是,因为如果是这样的话,那么Web应用之间就是透明的了。实际情况显然不是这样的。
  实际的情况是,在Web容器刚启动时,会启动Web容器类加载器来加载Web容器,然后为每个应用创建派生自Web容器的类加载器去加载Web应用。这样的话,Web应用能访问到Web容器的资源(比如说,Web容器的类库),但他们之间却是隔离的。
  从Java1.2之后,Java提供了一个新的ClassLoader:context classloader。context classloader是与线程绑在一起的,可以通过Thread的setContextLoader进行设置,通过getContextLoader来获得。回到之前的那个场景,当Web容器接受到Http请求,它会将加载Web容器的ClassLoader与这个线程关联起来,当Web容器完成了前期工作,需要将Http请求交给Web应用,跨过应用程序边界(cross the application boundary)时,Web容器会将加载处理请求的Web应用的ClassLoader与这个线程关联起来。当Web应用处理完请求,又交还给Web容器时,Web容器再次将context classloader设回加载Web容器的ClassLoader。

  以上这些只是我对ClassLoader的个人理解,如果你正在处理这方面的问题,请不要相信我,你可以去看看IBM DeveloperWorld上的这篇文章,我觉得还是不错的。如果你对ClassLoader有更深入的见解,觉得我说的不对,希望你能留言交流一下,谢谢。