加入收藏 | 设为首页 | 会员中心 | 我要投稿 好传媒网 (https://www.haochuanmei.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

Java的神秘世界:为何说ClassLoader 是 Java最神秘的技术之一

发布时间:2019-10-14 02:15:02 所属栏目:优化 来源:java互联网高级架构
导读:ClassLoader 是 Java 届最为神秘的技术之一,无数人被它伤透了脑筋,摸不清门道究竟在哪里。网上的文章也是一篇又一篇,经过本人的亲自鉴定,绝大部分内容都是在误导别人。本文我带读者彻底吃透 ClassLoader,以后其它的相关文章你们可以不必再细看了。 Cl

值得注意的是图中的 ExtensionClassLoader 的 parent 指针画了虚线,这是因为它的 parent 的值是 null,当 parent 字段是 null 时就表示它的父加载器是「根加载器」。如果某个 Class 对象的 classLoader 属性值是 null,那么就表示这个类也是「根加载器」加载的。注意这里的 parent 不是 super 不是父类,只是 ClassLoader 内部的字段。

Class.forName

当我们在使用 jdbc 驱动时,经常会使用 Class.forName 方法来动态加载驱动类。

  1. Class.forName("com.mysql.cj.jdbc.Driver"); 

其原理是 mysql 驱动的 Driver 类里有一个静态代码块,它会在 Driver 类被加载的时候执行。这个静态代码块会将 mysql 驱动实例注册到全局的 jdbc 驱动管理器里。

  1. class Driver { 
  2.  static { 
  3.  try { 
  4.  java.sql.DriverManager.registerDriver(new Driver()); 
  5.  } catch (SQLException E) { 
  6.  throw new RuntimeException("Can't register driver!"); 
  7.  } 
  8.  } 
  9.  ... 

forName 方法同样也是使用调用者 Class 对象的 ClassLoader 来加载目标类。不过 forName 还提供了多参数版本,可以指定使用哪个 ClassLoader 来加载

  1. Class<?> forName(String name, boolean initialize, ClassLoader cl) 

通过这种形式的 forName 方法可以突破内置加载器的限制,通过使用自定类加载器允许我们自由加载其它任意来源的类库。根据 ClassLoader 的传递性,目标类库传递引用到的其它类库也将会使用自定义加载器加载。

自定义加载器

ClassLoader 里面有三个重要的方法 loadClass()、findClass() 和 defineClass()。

loadClass() 方法是加载目标类的入口,它首先会查找当前 ClassLoader 以及它的双亲里面是否已经加载了目标类,如果没有找到就会让双亲尝试加载,如果双亲都加载不了,就会调用 findClass() 让自定义加载器自己来加载目标类。ClassLoader 的 findClass() 方法是需要子类来覆盖的,不同的加载器将使用不同的逻辑来获取目标类的字节码。拿到这个字节码之后再调用 defineClass() 方法将字节码转换成 Class 对象。下面我使用伪代码表示一下基本过程

  1. class ClassLoader { 
  2.  // 加载入口,定义了双亲委派规则 
  3.  Class loadClass(String name) { 
  4.  // 是否已经加载了 
  5.  Class t = this.findFromLoaded(name); 
  6.  if(t == null) { 
  7.  // 交给双亲 
  8.  t = this.parent.loadClass(name) 
  9.  } 
  10.  if(t == null) { 
  11.  // 双亲都不行,只能靠自己了 
  12.  t = this.findClass(name); 
  13.  } 
  14.  return t; 
  15.  } 
  16.   
  17.  // 交给子类自己去实现 
  18.  Class findClass(String name) { 
  19.  throw ClassNotFoundException(); 
  20.  } 
  21.   
  22.  // 组装Class对象 
  23.  Class defineClass(byte[] code, String name) { 
  24.  return buildClassFromCode(code, name); 
  25.  } 
  26. class CustomClassLoader extends ClassLoader { 
  27.  Class findClass(String name) { 
  28.  // 寻找字节码 
  29.  byte[] code = findCodeFromSomewhere(name); 
  30.  // 组装Class对象 
  31.  return this.defineClass(code, name); 
  32.  } 

自定义类加载器不易破坏双亲委派规则,不要轻易覆盖 loadClass 方法。否则可能会导致自定义加载器无法加载内置的核心类库。在使用自定义加载器时,要明确好它的父加载器是谁,将父加载器通过子类的构造器传入。如果父类加载器是 null,那就表示父加载器是「根加载器」。

  1. // ClassLoader 构造器 
  2. protected ClassLoader(String name, ClassLoader parent); 

双亲委派规则可能会变成三亲委派,四亲委派,取决于你使用的父加载器是谁,它会一直递归委派到根加载器。

Class.forName vs ClassLoader.loadClass

这两个方法都可以用来加载目标类,它们之间有一个小小的区别,那就是 Class.forName() 方法可以获取原生类型的 Class,而 ClassLoader.loadClass() 则会报错。

  1. Class<?> x = Class.forName("[I"); 
  2. System.out.println(x); 
  3. x = ClassLoader.getSystemClassLoader().loadClass("[I"); 
  4. System.out.println(x); 
  5. --------------------- 
  6. class [I 
  7. Exception in thread "main" java.lang.ClassNotFoundException: [I 
  8. ... 

(编辑:好传媒网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读