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

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

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

项目管理上有一个著名的概念叫着「钻石依赖」,是指软件依赖导致同一个软件包的两个版本需要共存而不能冲突。

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

我们平时使用的 maven 是这样解决钻石依赖的,它会从多个冲突的版本中选择一个来使用,如果不同的版本之间兼容性很糟糕,那么程序将无法正常编译运行。Maven 这种形式叫「扁平化」依赖管理。使用 ClassLoader 可以解决钻石依赖问题。不同版本的软件包使用不同的 ClassLoader 来加载,位于不同 ClassLoader 中名称一样的类实际上是不同的类。下面让我们使用 URLClassLoader 来尝试一个简单的例子,它默认的父加载器是 AppClassLoader

  1. $ cat ~/source/jcl/v1/Dep.java 
  2. public class Dep { 
  3.     public void print() { 
  4.         System.out.println("v1"); 
  5.     } 
  6. $ cat ~/source/jcl/v2/Dep.java 
  7. public class Dep { 
  8.  public void print() { 
  9.  System.out.println("v1"); 
  10.  } 
  11. $ cat ~/source/jcl/Test.java 
  12. public class Test { 
  13.     public static void main(String[] args) throws Exception { 
  14.         String v1dir = "file:///Users/qianwp/source/jcl/v1/"; 
  15.         String v2dir = "file:///Users/qianwp/source/jcl/v2/"; 
  16.         URLClassLoader v1 = new URLClassLoader(new URL[]{new URL(v1dir)}); 
  17.         URLClassLoader v2 = new URLClassLoader(new URL[]{new URL(v2dir)}); 
  18.          
  19.  Class<?> depv1Class = v1.loadClass("Dep"); 
  20.         Object depv1 = depv1Class.getConstructor().newInstance(); 
  21.         depv1Class.getMethod("print").invoke(depv1); 
  22.         Class<?> depv2Class = v2.loadClass("Dep"); 
  23.         Object depv2 = depv2Class.getConstructor().newInstance(); 
  24.         depv2Class.getMethod("print").invoke(depv2); 
  25.       
  26.  System.out.println(depv1Class.equals(depv2Class)); 
  27.  } 

在运行之前,我们需要对依赖的类库进行编译

  1. $ cd ~/source/jcl/v1 
  2. $ javac Dep.java 
  3. $ cd ~/source/jcl/v2 
  4. $ javac Dep.java 
  5. $ cd ~/source/jcl 
  6. $ javac Test.java 
  7. $ java Test 
  8. v1 
  9. v2 
  10. false 

在这个例子中如果两个 URLClassLoader 指向的路径是一样的,下面这个表达式还是 false,因为即使是同样的字节码用不同的 ClassLoader 加载出来的类都不能算同一个类

  1. depv1Class.equals(depv2Class) 

我们还可以让两个不同版本的 Dep 类实现同一个接口,这样可以避免使用反射的方式来调用 Dep 类里面的方法。

  1. Class<?> depv1Class = v1.loadClass("Dep"); 
  2. IPrint depv1 = (IPrint)depv1Class.getConstructor().newInstance(); 
  3. depv1.print() 

ClassLoader 固然可以解决依赖冲突问题,不过它也限制了不同软件包的操作界面必须使用反射或接口的方式进行动态调用。Maven 没有这种限制,它依赖于虚拟机的默认懒惰加载策略,运行过程中如果没有显示使用定制的 ClassLoader,那么从头到尾都是在使用 AppClassLoader,而不同版本的同名类必须使用不同的 ClassLoader 加载,所以 Maven 不能完美解决钻石依赖。 如果你想知道有没有开源的包管理工具可以解决钻石依赖的,我推荐你了解一下 sofa-ark,它是蚂蚁金服开源的轻量级类隔离框架。

分工与合作

(编辑:好传媒网)

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

热点阅读