Three principles of Classloader operation(Classloader操作三原则)

原文是从J2EE的书中摘取的一段关于ClassLoader的介绍。


-----------------------------------------------------------------------正文如下----------------------------------------------------------------------------------

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


Figure 21.1 Classloader hierarchy illustrating the delegation

委托原则(Delegation Principle):

如果一个类还没有被加载,一个类加载器会委托它的父亲加载器去加载它。这种委托会一直延续,直到到达委托层次的最顶层,由原始的类加载器加载完成该类。Figure21.1展示了这种情况。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成功加载了它。


Figure 21.2 Classloader hierarchy illustrating the delegation when classes cannot be found

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


Figure 21.3 Classloader hierarchy  and  classes visibility



可见性原则(Visibility principle)

被父亲类加载器加载的类对于孩子加载器是可见的,但关系相反相反则不可见。这说明,一个类只能看见它自己的加载器或者这个加载器的父类加载器加载的类,反过来是不可以的,比如,被ClassX的父亲加载器加载的类是不能看见ClassX的。为了更清楚的理解,让我们来看一个例子,如图2.3展示了四个classlodaer。ClassLoaderA是最顶层的classloader,B是它的孩子。ClassLodaerX 和Y是B 的孩子。他们各自都加载了与自己同名的类。ClassLoaderA能看见A类,CLassloaderB能看见A,B类。类似的,X能看见A,B,X,Y能看见A,B,Y。兄弟之间的类也是不可见的。




独特性原则Uniqueness Principle

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

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

Listing 21.2 Using Class.forName()
01 public class X {
02
03   public X() {
04      ClassLoader cl = this.getClass().getClassLoader();
05      Class B = Class.forName(“B”, true, cl);
06   }
07 }

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

Delegation Principle: If a class is not loaded already, the classloaders delegate the request to load that class to their parent classloaders.

Visibility Principle: Classes loaded by parent classloaders are visible to child classloaders but not vice versa.

Uniqueness Principle: When a classloader loads a class, the child classloaders in the hierarchy will never reload that class.

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


误解:

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


更具体的关系parent-child relation的介绍和编程 请见另外一篇博文:《ClassLoader编程实践


阅读更多
个人分类: java
上一篇从操作系统的角度 驳斥 杜车别的黑客帝国影评
下一篇JAR文件操作基础(Using JAR Files: The Basics)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭