zoukankan      html  css  js  c++  java
  • 类加载器操作三原则[译]

    (出自一本 J2EE 的教材中关于类加载器的介绍,原文已不知所踪。)

    类加载的 bug,一旦在编程中遇到很难调试。好消息是,理解类加载的过程中,我们只需要牢记住三条基本原则。如果你能清晰的理解这三条基本原则,所有问题都迎刃而解。下面,我们开始介绍。

    委托原则(Delegation Principle)

    如果一个类还没有被加载,类加载器会委托它的父亲加载器去加载它。

    这种委托会一直延续,直到到达委托层次的最顶层,由原始的类加载器加载完成该类。下图展示了这种情况。

    Systm-ClassPath classloader 加载了 MyApp.MyApp,而这个类创造了一个 java.util.Vector。假设现在 java.util.Vector 还没又被加载。因为 System-Classpath classloader 加载了 MyApp 类,它首先请求它的父亲 extension classloader 来加载这个类(java.util.Vector)。而 extension classloader 又请求 Bootstrap classloader 尝试加载。因为 java.util.Vector 是 J2SE 类,bootstrap classloader 成功加载了它。

    考虑一个当略微不同的情况,如下图。

    在这种情况中,MyApp 创造了一个新的用户自定义类的实例,MyClass。假设 MyClass 还没有被加载。像以往一样,当 System-Classpath classloader 接收到这个加载请求,它委托了它的父亲。最终这个委托传递到了 Bootstrap classloader。但是在 java 核心 API 里,找不到这个类。所以它的孩子加载器 Extensions classloader 尝试加载它。同样的,Extensions classloader 也没有找到它。最终,委托请求回到了 System-Classpath classloader 这里。它找到了这个类并加载成功。

    可见性原则(Visibility principle)

    被父亲类加载器加载的类对于孩子加载器是可见的,但关系相反相反则不可见。

    这说明,一个类只能看见它自己的加载器或者这个加载器的父类加载器加载的类,反过来是不可以的。比如,被 ClassX 的父亲加载器加载的类是不能看见 ClassX 的。为了更清楚的理解,让我们来看一个例子,如下图。

    图中展示了四个类加载器。类加载器 A 是最顶层的加载器,B 是它的孩子。类加载器 X 和 Y 是 B 的孩子。他们各自都加载了与自己同名的类。类加载器 A 能看见 A 类,类加载器 B 能看见 A,B 类。类似的,X 能看见 A,B,X,Y 能看见 A,B,Y。但兄弟、Y 之间的类是不可见的。

    独特性原则(Uniqueness Principle)

    当一个类加载器加载一个类时,它的孩子加载器绝不会重新加载这个类。

    这是因为委托原则中,一个加载器总是会委托自己的父亲加载器加载类。当层次中的父亲加载器无法加载类的时候,孩子类加载器就会(或者尝试去)加载这个类。这样,类加载的独特性就得到了保障。当父亲和孩子加载器加载了同一个类,一个有趣的情况就出现了。你可能会想这怎么可能出现?这不是违反了独特性原则?

    我们用可见性原则中的示例图来解释这个问题。我们假设没有任何类被加载到这些类加载器的层次结构中。假设 X 类被类加载器 X 加载,它强制性的用类加载器 X 加载 B 类。这可以通过像 Class.Name()这样的 API 来实现,代码如下:

    1
    2
    3
    4
    5
    6
    7
    
     public class X {
    
       public X() {
          ClassLoader cl = this.getClass().getClassLoader();
          Class B = Class.forName(B, true, cl);
       }
    }
    

    在 X 的构造函数中,B 被显示的使用类加载器 X 加载。如果另一个被类加载器 B 加载的类需要访问 B 类,则无法实现,因为委托原则只能向父亲方向查询。如果类加载器 B 也加载了 B 类,当比较两个 B 类的实例时,如果一个实例来自于类加载器 X,一个来自于类加载器 B,则会抛出 ClassCastException 异常。

    总结

    这三个原则 是解决程序中遇到的类加载问题的关键所在。在实际的编程中,并不需要显示的调用到类加载器,它主要出现在一些框架的代码中。但对于每一个开发者、架构师而言,都必须理解类加载的层次结构,这样才能写出优雅的代码。

    PS

    注意,虽然 java 的加载实现中,对于 bootstrap classloader 、extensions classloader 和 system classloader 来说,他们的关系是 parent-first,也就是像原则一中所说的那样,需要向上委托,但用户自定义的 classloader 完全可以跳出这个圈子,自己实现 parent-lastclassloader。比如 Websphere 中就有相关配置。

    更具体的类加载器编程实例,请见另外一篇博文:《Java 类加载器编程实践》

  • 相关阅读:
    第一部分 类的初始
    第一部分 数据内存存储、常预定义值类型、用户自定义值类型、预定义引用类型
    第一部分 变量、数据类型
    第一部分 程序结分析、控制台输入输出
    第一部分 代码组织概念,集成开发环境(IDE)
    第一部分 记事本搞定第一个C#程序和编译过程剖析
    jQuery函数
    leaflet入门(五)API翻译(下)
    leaflet入门(四)API翻译(上)
    禁止浏览器缓存js
  • 原文地址:https://www.cnblogs.com/biaobiaoqi/p/3308048.html
Copyright © 2011-2022 走看看