免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2559 | 回复: 1
打印 上一主题 下一主题

Java classloader的体系结构 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-05-09 11:49 |只看该作者 |倒序浏览
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 汉妮威记忆枕 整理,收集辛苦,希望能保留出处。

论坛徽章:
0
2 [报告]
发表于 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就更好啦,哈哈哈
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP