java classloader

什么是类加载器

  与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM,然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现的,它负责加载核心JavaClass(一般是加载存放在JAVA_HOME/jre/lib下的jar文件和JAVA_HOME/jre/classes下类,在Sun的JVM中,在执行java的命令中使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值可以指定附加的类)。另外JVM还会提供两个ClassLoader,它们都是用Java语言编写的,由BootstrapClassLoader加载;其中Extension ClassLoader负责加载扩展的Javaclass(一般是加载存放在JAVA_HOME/JRE/ext目录下的 类),ApplicationClassLoader负责加载来自在命令java中的-classpath或者java.class.path系统属性或者CLASSPATH*作系统属性所指定的JAR类包和类路径。当运行一个程序的时候,JVM启动,运行bootstrapclassloader,该 ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用 ExtClassLoader加载扩展API,最后AppClassLoader加载指定目录下的Class。

  classloader 加载类用的是全盘负责委托机制。所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;委托机制则是先让parent(父)类加载器 (而不是super,它与parent classloader类不是继承关系)寻找,只有在parent找不到的时候才从自己的类路径中去寻找。此外类加载还采用了cache机制,也就是如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必 须重新启动JVM才能生效的原因。

自定义classloader

  从java2开始,自定义classloader,只需要继承抽象类ClassLoader,然后实现findClass就行了,例如如下代码

  

protected Class<?> findClass(String name) throws ClassNotFoundException {
  try {
   byte[] data = 加载class字节码
   return defineClass(name, data, 0, data.length);
  } catch (ClassNotFoundException e) {
   return super.findClass(name);
  }
 }

 

使用如下:

 MyClassLoader loader = new MyClassLoader();
//  Class clazz = loader.loadClass("com.kingdee.eas.LoadedClazz");
  Class clazz = Class.forName("com.kingdee.eas.LoadedClazz",true,loader);

Current classloader vs Thread context classloader

  Current ClassLoader:当前类所属的ClassLoader,在虚拟机中类之间引用,默认就是使用这个ClassLoader。另外,当你使用Class.forName(), Class.getResource()这几个不带ClassLoader参数的方法是,默认同样适用当前类的ClassLoader。你可以通过方法XX.class.GetClassLoader()获取。

  Thread Context ClassLoader,每一个Thread有一个相关联的Context ClassLoader(由native方法建立的除外),可以通过Thread.setContextClassLoader() 方法设置。如果你没有主动设置,Thread默认集成Parent Thread的 Context ClassLoader(注意,是parent Thread 不是父类)。如果你整个应用中都没有对此作任何处理,那么 所有的Thread都会以System ClassLoader作为Context ClassLoader。知道这一点很重要,因为从web服务器,java企业服务器使用一些复杂而且精巧的ClassLoader结构去实现诸如 JNDI、线程池和热部署等功能以来,这种简单的情况越发的少见了。

  Context ClassLoader提供一个突破委托代理机制的后门。虚拟机通过父子层次关系组织管理ClassLoader,每个ClassLoader都有一个 Parent ClassLoader(BootStartp不在此范围之内),当要求一个ClassLoader装载一个类时,他首先请求Parent ClassLoader去装载,只有parent ClassLoader装载失败,才会尝试自己装载。但是,某些时候这种顺序机制会造成困扰,特别是jvm需要动态载入由开发者提供的资源时。就以JNDI为例,JNDI的类是由bootstarp ClassLoader从rt.jar中间载入的,但是JNDI具体的核心驱动是由正式的实现提供的,并且通常会处于-cp参数之下(注:也就是默认的 System ClassLoader管理),这就要求bootstartp ClassLoader去载入只有SystemClassLoader可见的类,正常的逻辑就没办法处理。怎么办呢?parent可以通过获得当前调用Thread的方法获得调用线程的Context ClassLoder 来载入类。

 

 

posted @ 2012-09-17 17:09  cprime  阅读(198)  评论(0编辑  收藏  举报