Java双亲委派模型

Java双亲委派模型详解

我们在了解双亲委派模型之前,不得不先了解一下什么是类加载器。虚拟机设计团队之初是希望类加载过程“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作能放到虚拟机外部实现,以便于让程序自己决定如何获取该类,实现这个动作的代码的工具成为类加载器。

可能很多人觉得类加载器,顾名思义,就是个加载类的嘛,有啥大不了的,但是类加载这个过程是很严格的,对于任意一个类,我们都需要由加载他的类加载器和类的本身来决定该类在虚拟机之中的唯一性。什么意思呢??就是说我们的虚拟机要比较两个类是否相等,那前提条件是就是这两个类必须是在同一个类加载器加载的,如果两个类都不是由同一个加载器加载的,那么这俩类就一定不相等,所以就没有比较的意义!


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {

        ClassLoader myLoader = new ClassLoader() {

            @Override

            public Class<?> loadClass(String name) throws ClassNotFoundException {

                try {

                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";

                    InputStream is = getClass().getResourceAsStream(fileName);

                    if (is == null) {

                        return super.loadClass(name);

                    }

                    byte[] b = new byte[is.available()];

                    is.read(b);

                    return defineClass(name, b, 0, b.length);

                } catch (IOException e) {

                    throw new ClassNotFoundException(name);

                }

            }

        };

        Object obj = myLoader.loadClass("org.fenixsoft.classloading.ClassLoaderTest").newInstance();

        System.out.println(obj.getClass());

        System.out.println(obj instanceof org.fenixsoft.classloading.ClassLoaderTest);

    }

}

  就比如上面这段代码,代码运行结果很令人失望,虽然打印出的类路径是相同的,但是比较后的结果却是false,这是为啥啊?好气啊,明明类路径一样,但是结果却不同?

这是因为,我们自己实现了属于我们自己的类加载器,我们选择了我们自己的加载路径去加载该类,而另一个同类路径名的类却是由另一个加载器(应用程序类加载器)加载的,只要不是同一个类加载器加载的类,一定不是同一个类!!

双亲委派模型

从java虚拟机角度来讲,只存在两种不同的类加载器:

(1)一种是启动类加载器,由C++语言实现的,属于虚拟机的一部分;

(2)一种是所有的其他类加载器,这些都是由Java实现的,独立于虚拟机外部,继承自java.lang.ClassLoader;

但是从开发人员角度来讲,应该分的再细一些,绝大部分程序都使用到了以下三种系统提供的类加载器:

(1)启动类加载器,该加载器是C++实现的,它负责加载存放于<JAVA_HOME>\lib目录下的类,它是仅仅按照文件的名字来识别的,名字不符合的类就算放到该目录下,也是毫无卵用的.....

(2)扩展类加载器,它是负责加载<JAVA_HOME>\lib\Ext目录下的;

(3)应用程序类加载器,这个类也被称为系统类加载器,它是负责用户类路径classpath上指定的类库,开发者可以直接使用这个加载器;

应用程序都是由这三种加载器相互配合进行加载的,有必要的话,还可以实现属于自己的类加载器,这几种加载器关系如图:

这种层次结构我们就称之为双亲委派模型,可以很直观的看出除了顶层的启动类加载器外,其他的都有属于自己的父类加载器。但是我们在这里不要混淆一个概念,就是继承(Inheritance),这个结构图并不是继承关系而是通过组合的方式来实现向上委托的.......

双亲委派的工作流程就是:如果一个类加载器收到了类加载的请求,它是不会自己立马亲自动手去加载的(比较懒,哈哈!),而是把该请求委托给父类,每一层都是如此,到了顶层后,这时就无法再向上传递了,所有的请求都集中到了启动类加载器,当父类反馈自己无法满足这个请求时,这时就会再把请求一层层向下传递。

这样的好处是啥??相信大家看这种层次结构应该很清晰,但是这有什么意义吗?比如java.lang.Object,他是存在rt.jar里的,不论哪种加载器,是系统自带的也好还是我们自己实现的也好,都会把请求一层层的往上委托,直到启动类加载器,而启动类加载器一看,自己是有这个类的,所以加载,因此Object在程序的各个类加载器的加载下永远都是同一个类。反之,没有双亲委派模型,任由各个类加载器自己去加载的话,比如我们开发者自己写了Object类,包名也是java.lang,那么系统中就会出现各种各样的Object,每一个层级的类加载器都加载了自己具有个性的Object,那么作为程序中这么基础这么重要的Object,他的唯一性得不到保证,应用程序就会杂乱不堪。

双亲委派模型的作用想必到这里很多人应该清楚了,觉得:“哇!这个模型还真的是很强大呢...”。他的实现也是非常简单的:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{

    //check the class has been loaded or not

    Class c = findLoadedClass(name);

    if(c == null){

        try{

            if(parent != null){

                c = parent.loadClass(name,false);

            }else{

                c = findBootstrapClassOrNull(name);

            }

        }catch(ClassNotFoundException e){

            //if throws the exception ,the father can not complete the load

        }

        if(c == null){

            c = findClass(name);

        }

    }

    if(resolve){

        resolveClass(c);

    }

    return c;

}

从图中的代码,我们大致可以看出这个委托机制是如何实现的,当一个加载器收到请求后,首先会判断一下当前这个类是否已经被加载过,如果没有被加载的话,开始委托父类加载器了(就是这么懒,哈哈),如果没有父类的话,就默认使用启动类加载器。如果抛异常了,就代表当前类加载器的父类无法加载,满足不了请求,那么此时只能自己亲自出马了!!所以什么事还是自己来做的靠谱啊哈哈。

总结

当然,这种模型一直都不是强制性的,而是推荐我们这么做的,往年就出现过打破该机制的事件,典型的例子就是JNDI服务,他的代码是交给启动类加载器去实现的,但是当JNDI要对资源进行集中化管理时,他需要调用其他公司实现并部署在应用程序的classpath下的JNDI接口,因为这些代码是需要我们开发者自己来实现的,这时启动类加载器是无法识别这些类的,于是乎出现了一种线程上下文加载器,JNDI服务可以调用该加载器去加载所需要的代码,就是通过父类加载器去请求子类加载器来实现的,这已经很明显的违背了双亲委派模型。

原文地址:https://www.cnblogs.com/sxc1991/p/9221396.html

时间: 2024-11-03 07:08:46

Java双亲委派模型的相关文章

java双亲委派模型如何递归实现

以下是Classloader的中最重要的方法,也就是所谓的双亲委派模型.这个模型第一次在周志明的书上看到,当时看了只知道是类加载过程是首先是委托给父加载器,否则父不能加载,则自己加载,整个过程实则是一个很简单的递归过程,本文以实例讲解这个模型到底是咋实现的? protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadin

《深入理解Java虚拟机》笔记 第七章 虚拟机加载机制及双亲委派模型

? ? 站在虚拟机的角度上,只存在两种不同的类加载器: ? ? 一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分: ? ? 另外一种就是其它所有的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader. ? 从Java开发人员的角度看,类加载器还可以划分得更细一些,如下: ? ? 1.启动类加载器(Bootstrap ClassLoader):这个类加载器负责将放

Java自定义类加载器与双亲委派模型

其实,双亲委派模型并不复杂.自定义类加载器也不难!随便从网上搜一下就能搜出一大把结果,然后copy一下就能用.但是,如果每次想自定义类加载器就必须搜一遍别人的文章,然后复制,这样显然不行.可是自定义类加载器又不经常用,时间久了容易忘记.相信你经常会记不太清loadClass.findClass.defineClass这些函数我到底应该重写哪一个?它们主要是做什么的?本文大致分析了各个函数的流程,目的就是让你看完之后,难以忘记!或者说,延长你对自定义类加载器的记忆时间!随时随地想自定义就自定义!

java虚拟机类加载机制和双亲委派模型

java虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型. 类的生命周期是从类被加载到虚拟机内存中,到卸载出内存为止: 类的生命周期: 加载 loading . 验证 verification. 准备 preparation. 解析 resolution. 初始化 initialization. 使用 using. 卸载 unloading 类加载器的层次结构: 双亲委派模型过程: 某个特定的类加载器

Java虚拟机详解(十一)------双亲委派模型

在上一篇博客,我们介绍了类加载过程,包括5个阶段,分别是“加载”,“验证”,“准备”,“解析”,“初始化”,如下图所示: 本篇博客,我们来介绍Java虚拟机的双亲委派模型,在介绍之前,我先抛出一个问题: 我们知道,在JDK源码中,有各种Java自带的类,比如java.lang.String,java.util.List等,那么我们自己的项目中,能够写一个命名为java.lang.String.java 等JDK源码中存在的类,并且在项目中使用吗? 1.类加载器 什么是类加载器?上篇博客我们介绍类

jvm类加载器和双亲委派模型

类加载器按照层次,从顶层到底层,分为以下三种: (1)启动类加载器(Bootstrap ClassLoader) 这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中.启动类加载器无法被Java程序直接引用. (2)扩展类加载器(Extension ClassLoader) 这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有

双亲委派模型

双亲委派模型的概念 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因为所有的类请求都会传给顶层的启动类加载器(Bootstrap ClassLoader),只有当父加载器反馈自己无法完成该加载请求时,子加载器才会尝试自己去加载. 双亲委派模型机制图 加载类 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中.由于引导类

【深入理解JVM】类加载器与双亲委派模型

原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p/4138511.html 加载类的开放性 类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因.在类加载的第一阶段"加载"过程中,需要通过一个类的全限定名来获取定义此类的二进制字节流,完成这个动作的代码块就是类加载器.这一动作是放在Java虚拟机外部

类的加载和双亲委派模型

类加载器基本概念 顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中.一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件).类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例. 任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性. 类加载器类型 启动类(引导类)