qingqing3721 发表于 2011-05-09 11:49

Java classloader的体系结构

Bootstrap ClassLoader/启动类加载器
  主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。
  Extension ClassLoader/扩展类加载器
  主要负责jdk_home/lib/ext目录下的jar包或 -Djava. ext. dirs 指定目录下的jar包装入工作。
  System ClassLoader/系统类加载器
  主要负责java -classpath/-Djava. class. path所指的目录下的类与jar包装入工作。
  User Custom ClassLoader/用户自定义类加载器(java. lang. ClassLoader的子类)
  在程序运行时期,通过java. lang. ClassLoader的子类动态加载class文件,体现java动态实时类装入特性。
  类加载器的特性:
  每个ClassLoader都维护了一份自己的称号空间,同一个称号空间里不能出现两个同名的类。
  为了实现java安全沙箱模型顶层的类加载器安全机制,java默许采用了 " 双亲委派的加载链 " 结构。
  类图中,BootstrapClassLoader是一个单独的java类,其实在这里,不应该叫他是一个java类。 由于, 它已经完全不用java实现了。 它是在jvm启动时,就被结构起来的,负责java平台核心库。
  自定义类加载器加载一个类的步骤
  ClassLoader 类加载逻辑剖析,以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:
  // 反省类能否已被装载过
  Class c = findLoadedClass(name);
  if (c == null ) {
  // 指定类未被装载过
  try {
  if (parent != null ) {
  // 如果父类加载器不为空,则委派给父类加载
  c = parent. loadClass(name,false );
  } else {
  // 如果父类加载器为空,则委派给启动类加载加载
  c = findBootstrapClass0(name);
  }
  } catch (ClassNotFoundException e) {
  // 启动类加载器或父类加载器抛出异常后,当前类加载器将其
  // 捕获,并通过findClass办法,由自身加载
  c = findClass(name);
  }
  }
  线程上下文类加载器
  java默许的线程上下文类加载器是 系统类加载器(AppClassLoader)。
  // Now create the class loader to use to launch the application
  try {
  loader = AppClassLoader. getAppClassLoader(extcl);
  } catch (IOException e) {
  throw new InternalError(
  "Could not create application class loader" );
  }
  // Also set the context class loader for the primordial thread.
  Thread. currentThread(). setContextClassLoader(loader);
  Java代码
  // Now create the class loader to use to launch the application
  try {
  loader = AppClassLoader. getAppClassLoader(extcl);
  } catch (IOException e) {
  throw new InternalError(
  "Could not create application class loader" );
  }
  // Also set the context class loader for the primordial thread.
  Thread. currentThread(). setContextClassLoader(loader);
  以上代码摘自sun. misc. Launch的无参结构函数Launch()。
  运用线程上下文类加载器,可以在执行线程中,抛弃双亲委派加载链模式,运用线程上下文里的类加载器加载类.
  典型的例子有,通过线程上下文来加载第三方库jndi实现,而不依赖于双亲委派.
  大局部java app效劳器(jboss,tomcat. . )也是采用contextClassLoader来处置web效劳。
  还有一些采用 hotswap 特性的框架,也运用了线程上下文类加载器,比方 seasar (full stack framework in japenese).
  线程上下文从基本解决了普通应用不能违犯双亲委派模式的问题.
  使java类加载体系显得更灵敏.
  随着多核时代的降临,置信多线程开发将会越来越多地进入程序员的实际编码过程中.因此,
  在编写基础设施时,通过运用线程上下文来加载类,应该是一个很好的选择。
  当然,好东西都有利弊.运用线程上下文加载类,也要注意,保证多根需求通信的线程间的类加载器应该是同一个,
  避免由于不同的类加载器,招致类型转换异常(ClassCastException)。
  为什么要运用这种双亲委托模式呢?
  由于这样可以避免重复加载, 当父亲已经加载了该类的时分, 就没有必要子ClassLoader再加载一次。
  思索到安全因素, 我们试想一下, 如果不运用这种委托模式, 那我们就可以随时运用自定义的String来动态替代java核心api中定义类型, 这样会存在十分大的安全隐患, 而双亲委托的方式, 就可以避免这种情况, 由于String已经在启动时被加载, 所以用户自定义类是无法加载一个自定义的ClassLoader。
  java动态载入class的两种方式:
  implicit隐式, 即利用实例化才载入的特性来动态载入class
  explicit显式方式, 又分两种方式:
  java. lang. Class的forName()办法
  java. lang. ClassLoader的loadClass()办法
  用Class. forName加载类
  Class. forName运用的是被调用者的类加载器来加载类的。
  这种特性,证明了java类加载器中的称号空间是独一的,不会相互干扰。
  即在普通情况下,保证同一个类中所关联的其他类都是由当前类的类加载器所加载的。
  public static Class forName(String className)
  throws ClassNotFoundException {
  return forName0(className,true ,ClassLoader. getCallerClassLoader());
  }
  /** Called after security checks have been made.*/
  private static native Class forName0(String name,boolean initialize,
  ClassLoader loader)
  throws ClassNotFoundException;
  Java代码
  public static Class forName(String className)
  throws ClassNotFoundException {
  return forName0(className,true ,ClassLoader. getCallerClassLoader());
  }
  /** Called after security checks have been made.*/
  private static native Class forName0(String name,boolean initialize,
  ClassLoader loader)
  throws ClassNotFoundException;
  下面中 ClassLoader. getCallerClassLoader 就是得到调用当前forName办法的类的类加载器
  static块在什么时分执行?
  当调用forName(String)载入class时执行, 如果调用ClassLoader. loadClass并不会执行. forName(String, false, ClassLoader)时也不会执行.
  如果载入Class时没有执行static块则在第一次实例化时执行. 比方new , Class. newInstance()操作
  static块仅执行一次
  各个java类由哪些classLoader加载?
  java类可以通过实例. getClass. getClassLoader()得知
  接口由AppClassLoader(System ClassLoader, 可以由ClassLoader. getSystemClassLoader()取得实例)载入
  ClassLoader类由bootstrap loader载入
  NoClassDefFoundError和ClassNotFoundException
  NoClassDefFoundError:当java源文件已编译成. class文件, 但是ClassLoader在运行时期在其搜寻途径load某个类时, 没有找到. class文件则报这个错
  ClassNotFoundException:试图通过一个String变量来创立一个Class类时不成功则抛出这个异常  原文出处:blog. chenlb/2009/06/java-classloader-architecture. html文章由 jiyizhen-buy.com 汉妮威记忆枕 整理,收集辛苦,希望能保留出处。

ladyABC 发表于 2012-08-09 23:42

嗯,总结的挺好。
有几处疑问,望与楼主探讨:
1. 例如,在A类的foo()方法当中,执行forName(B类); 那么B类是用A的classloader来加载呢?还是用当前线程的classloader来加载?二者,谁的优先级更高呢?
2. 命名空间里,一个类确实只能存在一份,但可以实现复写吧?就是我先加载了A,又加载了A',其中A与A'包路径及类名均相同(只是先后加载顺序不同),我想让A'生效。
3. 而如果A中正好有一个static块,那么是不是不幸的它也被执行了呢?尽管我不希望A被加载进来。
4. 再讲讲osgi就更好啦,哈哈哈
页: [1]
查看完整版本: Java classloader的体系结构