精华内容
下载资源
问答
  • 采用双亲委派模型好处是Java类随着它的类加载器一起具备了一种带有优先级的层级关系,通过这种层级关系可以避免类的重复加载. Bootstrap ClassLoader(启动类加载器):负责将%JAVA_HOME%/lib目录中或-...

            Java双亲委派模型是什么、优势在哪、双亲委派模型的破坏

    前言

    双亲委派模型是Java加载类的机制.采用双亲委派模型的好处是Java类随着它的类加载器一起具备了一种带有优先级的层级关系,通过这种层级关系可以避免类的重复加载.

    • Bootstrap ClassLoader(启动类加载器): 负责将%JAVA_HOME%/lib目录中或-Xbootclasspath中参数指定的路径中的,并且是虚拟机识别的(按名称)类库加载到JVM中
    • Extension ClassLoader(扩展类加载器): 负责加载%JAVA_HOME%/lib/ext中的所有类库
    • Application ClassLoader(应用程序加载器): 负责ClassPath中的类库

    概述

    双亲委派模式的工作原理的是;如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都不愿意干活,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己想办法去完成,这不就是传说中的双亲委派模式.那么这种模式有什么作用呢?

    双亲委派模式优势

        1 采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。

        2 其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

     

    两种类型的类加载器详解

    1、 JVM自带的类加载器(3种):
    (1)根类加载器(Bootstrap):
    a、C++编写的,程序员无法在程序中获取该类
    b、负责加载虚拟机的核心库,比如java.lang.Object
    c、没有继承ClassLoader类
    (2)扩展类加载器(Extension):
    a、Java编写的,从指定目录中加载类库
    b、父加载器是根类加载器
    c、是ClassLoader的子类
    d、如果用户把创建的jar文件放到指定目录中,也会被扩展加载器加载。
    (3)系统加载器(System)或者应用加载器(App):
    a、Java编写的
    b、父加载器是扩展类加载器
    c、从环境变量或者class.path中加载类
    d、是用户自定义类加载的默认父加载器
    e、是ClassLoader的子类

    2、用户自定义的类加载器:
    (1)Java.lang.ClassLoader类的子类
    (2)用户可以定制类的加载方式
    (3)父类加载器是系统加载器
    (4)编写步骤:
    A、继承ClassLoader
    B、重写findClass方法。从特定位置加载class文件,得到字节数组,然后利用defineClass把字节数组转化为Class对象
    (5)为什么要自定义类加载器?
    A、可以从指定位置加载class文件,比如说从数据库、云端加载class文件
    B、加密:Java代码可以被轻易的反编译,因此,如果需要对代码进行加密,那么加密以后的代码,就不能使用Java自带的ClassLoader来加载这个类了,需要自定义ClassLoader,对这个类进行解密,然后加载。

     

    源码

    1.首先加载类调用的loadClass方法,我们找到ClassLoader的loadClass():

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
     
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);
     
                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }复制代码

     

    • 首先判断了该类是否已加载.
    • 若没加载,则传给双亲加载器去加载,
    • 若双亲加载器没能成功加载它,则自己用findClass()去加载.所以是个向上递归的过程.
    • 自定义加载器时,需要重写findClass方法,因为是空的,没有任何内容:
     protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }复制代码

     


    双亲委派模型的破坏

    1.第一次破坏

    由于双亲委派模型是在JDK1.2之后才被引入的,而类加载器和抽象类java.lang.ClassLoader则在JDK1.0时代就已经存在,面对已经存在的用户自定义类加载器的实现代码,Java设计者引入双亲委派模型时不得不做出一些妥协。在此之前,用户去继承java.lang.ClassLoader的唯一目的就是为了重写loadClass()方法,因为虚拟机在进行类加载的时候会调用加载器的私有方法loadClassInternal(),而这个方法唯一逻辑就是去调用自己的loadClass()。

    2.第二次破坏

    双亲委派模型的第二次“被破坏”是由这个模型自身的缺陷所导致的,双亲委派很好地解决了各个类加载器的基础类的同一问题(越基础的类由越上层的加载器进行加载),基础类之所以称为“基础”,是因为它们总是作为被用户代码调用的API,但世事往往没有绝对的完美。

    如果基础类又要调用回用户的代码,那该么办?

    一个典型的例子就是JNDI服务,JNDI现在已经是Java的标准服务,
    它的代码由启动类加载器去加载(在JDK1.3时放进去的rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者的代码,但启动类加载器不可能“认识”这些代码。

    为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,他将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。

    有了线程上下文加载器,JNDI服务就可以使用它去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,这种行为实际上就是打通了双亲委派模型层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则,但这也是无可奈何的事情。Java中所有涉及SPI的加载动作基本上都采用这种方式,例如JNDI、JDBC、JCE、JAXB和JBI等。

    3.第三次破坏

    双亲委派模型的第三次“被破坏”是由于用户对程序动态性的追求导致的,这里所说的“动态性”指的是当前一些非常“热门”的名词:代码热替换、模块热部署等,简答的说就是机器不用重启,只要部署上就能用。
    OSGi实现模块化热部署的关键则是它自定义的类加载器机制的实现。每一个程序模块(Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉以实现代码的热替换。在OSGi幻境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构,当受到类加载请求时,OSGi将按照下面的顺序进行类搜索:
    1)将java.*开头的类委派给父类加载器加载。
    2)否则,将委派列表名单内的类委派给父类加载器加载。
    3)否则,将Import列表中的类委派给Export这个类的Bundle的类加载器加载。
    4)否则,查找当前Bundle的ClassPath,使用自己的类加载器加载。
    5)否则,查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。
    6)否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。
    7)否则,类加载器失败。

    自己编写

    1.首先需要一个编译好的class文件,笔者用了一个之前写的斐波那契的类Fib.class(所在路径:C:/Users/Think/crabapple),下面是用idea通过反编译方式打开的class文件,注意记下class文件的包名,在后续代码中需要使用类的全限定名称.

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    package crabapple;
     
    public class Fib {
      
        public static int fib(int num) {
            return num < 2 ? num : fib(num - 2) + fib(num - 1);
        }
    }复制代码

    2.继承ClassLoader,重写findClass方法:

    class MyClassLoader extends ClassLoader {
        private String classPath;  // 保存的地址
     
        /**
         * 传入地址构造函数
         * @param classPath
         */
        public MyClassLoader(String classPath) {
            this.classPath = classPath;
        }
     
        /**
         * 读取class文件
         * @param name
         * @return
         * @throws Exception
         */
        private byte[] loadByte(String name) throws Exception {
            String inPath = classPath + "/" + name + ".class";
            FileInputStream fis = new FileInputStream(inPath);
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }
     
        /**
         * 重写findClass方法,让加载的时候调用findClass方法
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                // 将字节码载入内存
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }复制代码
    • loadByte方法仅用作读取文件
    • findClass方法才是加载类到内存的,注意name必须填全限定名,比如java.lang.Object.

     3.测试,一下将使用一些反射机制和class类的方法

    public class ClassLoaderTest extends ClassLoader {
     
        //main函数本该抛出异常有 ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException,为了好看,简写成Exception
        public static void main(String[] args) throws Exception {
            //初始化类加载器
            MyClassLoader myClassLoader=new MyClassLoader("C:/Users/Think/crabapple");
            //加载Fib类,笔者class文件包名为crabapple
            Class myClass=myClassLoader.loadClass("crabapple.Fib");
            //获取加载类的实例
            Object object=myClass.newInstance();
            //获取该类一个名为fib,且参数为int的方法
            Method method=myClass.getMethod("fib",int.class);
            //执行这个方法
            int result=method.invoke(object,4);
            //打印结果
            System.out.print(result);
            
            //output
            /**
             * 3
             * Process finished with exit code 0
             */
        }
    }复制代码
    • 执行成功
    • 我们来分析下,Fib类的加载过程,初始化自定义类加载器后,loadClass方法肯定将其委派到双亲Application ClassLoader,而Application ClassLoader又将其委派到Extension ClassLoader,继而委派到Bootstrap ClassLoader.但是Bootstrap ClassLoader发现Fib并不在自己的加载能力范围内,于是移向Extension ClassLoader,同理Extension ClassLoader只能加载/ext中的class,继而让给Application ClassLoader,而Application ClassLoader只加载classpath中的类,于是又回到我们自定义的MyClassLoader,幸好我们重写了findClass方法进而执行了加载,否在findClass抛出找不到类的异常.至此Fib类加载完成.

     参考链接

    https://blog.csdn.net/weixin_34010566/article/details/91404531

    https://blog.csdn.net/codeyanbao/article/details/82875064

    https://mp.weixin.qq.com/s/H3ZGI9XIwrWGsByKbCYC7A

    展开全文
  • 双亲委派模型

    2018-07-30 21:55:30
    双亲委派模型要求除顶层启动类加载器外其余类加载器都应该有自己的父类加载器;类加载器之间通过复用关系来复用父加载器的代码。 双亲委派模型工作工程: 1.当Application ClassLoader 收到一个类加载请求时,他...

    原文地址: http://blog.csdn.net/inspiredbh/article/details/74889654

     

    Java虚拟机先从最核心的API开始查找,防止不可信的类扮演被信任的类。

    启动类加载器 Bootstrap ClassLoader:加载<JAVA_HOME>\lib目录下核心库

    扩展类加载器 Extension ClassLoader:加载<JAVA_HOME>\lib\ext目录下扩展包

    应用程序类加载器 Application ClassLoader:  加载用户路径(classpath)上指定的类库
     

     双亲委派模型


    双亲委派模型要求除顶层启动类加载器外其余类加载器都应该有自己的父类加载器;类加载器之间通过复用关系来复用父加载器的代码。

    双亲委派模型工作工程:
    1.当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。  

    2.当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。  

    3.如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。  

    4.如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。  

    5.如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。  

    6.如果均加载失败,就会抛出ClassNotFoundException异常。 

     

     双亲委派模型的实现过程:

    实现双亲委派模型的代码都集中在java.lang.ClassLoader的loadClass()方法中:  
    首先会检查请求加载的类是否已经被加载过;  
    若没有被加载过:  
    递归调用父类加载器的loadClass();  
    父类加载器为空后就使用启动类加载器加载;  
    如果父类加载器和启动类加载器均无法加载请求,则调用自身的加载功能。

    双亲委派模型的优点:

    Java类伴随其类加载器具备了带有优先级的层次关系,确保了在各种加载环境的加载顺序。  
    保证了运行的安全性,防止不可信类扮演可信任的类。

    展开全文
  • 双亲委派模型好处: ·主要是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String。·同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader...

    首先说一下双亲委派的好处
    双亲委派模型的好处:

    ·主要是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String。·同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader(类加载器)加载就是不同的两个类
    学好双亲委派谨记两点
    向上委派,为了查找缓存
    向下查找,是为了查找加载路径

    双亲委派模型图

    在这里插入图片描述

    展开全文
  • 虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的...双亲委派模型: 某个特定的类加载器在接到加载类的请求时,首先将加载任...

    虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
    类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
    家宴准姐出
    其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。
    在这里插入图片描述
    双亲委派模型:

    某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

    使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。

    展开全文
  • 双亲委派模型好处: 在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ...
  • 一文搞懂双亲委派模型

    万次阅读 多人点赞 2019-06-28 14:42:07
    由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。...
  • 双亲委派模型基本概念 定义: 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。 双亲委派模型的工作过程是: 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试...
  • 有效的破坏双亲委派模型实验代码 上面第一种是违背了双亲委派模型了,第二种则是保持双亲委派模型。但是效果是一样的,因为只要通过我们自己的 findClass() 方法,那么就可以达到破坏双亲委派模型的目的了。这里我...
  • 在说双亲委派模型之前首先得先了解一下类加载阶段。 类的加载阶段 类加载阶段分为加载、连接、初始化三个阶段,而加载阶段需要通过类的全限定名来获取定义了此类的二进制字节流。 Java特意把这一步抽出来用类加载...
  • java 的双亲委派模型

    2020-06-27 01:28:22
    Java加载的过程以及Class的 生命周期 类加载阶段: (1)Java虚拟机将.class文件读入内存,并为之创建一个Class对象。 ...(2)任何类被使用时系统都会为其创建一个且仅有一个Class对象。...双亲委派模..
  • JNDI服务使用这个线程上下文类加载器去加载所需的SPI服务代码,这是一种父类加载器去请求子类加载器完成类加载的行为,这种行为实际上是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型的...
  • 因此使用双亲委派模型来组织类加载器之间的关系,主要体现两个好处: 1.类伴随它的类加载器一起具备了一种带有优先级的层次关系,确保了在各种加载环境的加载顺序。 2.保证了运行的安全性,防止不可信类扮演可信任的...
  • 什么情况下需要破坏双亲委派模型

    万次阅读 2018-08-21 15:40:30
    双亲委派模型的破坏 双亲委派模型的第一次“被破坏”其实发生在双亲委派模型出现之前–即JDK1.2发布之前。由于双亲委派模型是在JDK1.2之后才被引入的,而类加载器和抽象类java.lang.ClassLoader则是JDK1.0时候就...
  • 类加载器的双亲委派模型在 JDK 1.2 时期被引入,并被广泛应用于此后几乎所有的 Java 程序中,但它并不是一个具有强制性的约束力的模型,而是 Java 设计者们推荐给开发者的一种加载器实现的最佳实践。 一、类加载器 ...
  • 目录1、前言2、SPI的概念2.1 典型应用:JDBC2.2 SPI机制的通俗理解3、双亲委派模型3.1 双亲委派模型好处4、为什么说SPI破坏了双亲委派模型4.1 可见性原则4.2 双亲委派模型的妥协5、结尾 1、前言 SPI?一开始接触这...
  • 类加载器和双亲委派模型

    千次阅读 2016-06-13 14:43:35
     双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承关系来实现,而是都使用组合关系来复用父加载器的代码  工作过程:  如果...
  •  双亲委派模型有个显而易见的好处就是,Java类随着它的类加载器有了一个优先级的层次关系,比如放在rt.jar下的java.lang.Object 无论哪个类想加载它都会传递到启动类加载器这里,用户永远无法加载一个自己编写的与...
  • 【深入理解JVM】:类加载器与双亲委派模型

    万次阅读 多人点赞 2016-05-06 19:09:25
    使用双亲委派模型好处在于 Java类随着它的类加载器一起具备了一种带有优先级的层次关系 。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ...
  • SPI是否破坏了双亲委派模型

    千次阅读 2019-12-30 17:34:31
    在之前答辩时,评委问了一个问题:SPI是否破坏了JVM中的双亲委派模型。本来自己以为对SPI有所了解,不料还是有很多需要学习的时候。今天就来探索一下类加载破坏双亲委派的问题,并以数据库驱动Driver为例子进行介绍...
  • 安全性:使用双亲委派模型来组织类加载器之间的关系,有一个很明显的好处,就是Java类随着它的类加载器(说白了,就是它所在的目录)一起具备了一种带有优先级的层次关系,这对于保证Java程序的稳定运作很重要。...
  • 双亲委派模型最大的好处就是让 Java类同其类加载器一起具备了一种带优先级的层次关系 。举个例子来说明下:比如我们要加载顶层的Java类——java.lang.Object类,无论我们用哪个类加载器去加载Object类,这个加载请求...
  • 1. 双亲委派模型是什么? 当某个类加载器需要加载某个.class字节码文件时,它首先把这个任务委托给它的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。 2. 双亲委派模型的工作...
  • 类加载-初始化(类的生命周期) 一个类的完整生命周期: 1. 加载过程 所谓加载,简而言之就是将 Java 类的字节...但是,如果想打破双亲委派模型则需要重写 loadClass() 方法 参考:javaGuide 《深入理解 Java 虚拟机》

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,242
精华内容 3,696
关键字:

双亲委派模型的好处