JVM – 彻底理解打破双亲委派机制

文章目录Pre双亲委派何为打破双亲委派演示Pre双亲委派JVM-白话聊一聊JVM类加载和双亲委派机制源码解析JVM-自定义类加载器何为打破双亲委派举个例子有个类Artisan我们希望通过自定义加载器直接从某个路径下读取Artisan.class.而不是说通过自定义加载器委托给AppClassLoader——>ExtClassLoader—->BootClassLoader这么走一遍,都没有的话,才让自定义加载器去加载Artis

大家好,又见面了,我是你们的朋友全栈君。

在这里插入图片描述

Pre 双亲委派

JVM-白话聊一聊JVM类加载和双亲委派机制源码解析

JVM – 自定义类加载器


何为打破双亲委派

举个例子 有个类 Artisan

我们希望通过自定义加载器 直接从某个路径下读取Artisan.class . 而不是说 通过自定义加载器 委托给 AppClassLoader ——> ExtClassLoader —-> BootClassLoader 这么走一遍,都没有的话,才让自定义加载器去加载 Artisan.class . 这么一来 还是 双亲委派。

我们期望的是 Artisan.class 及时在 AppClassLoader 中存在,也不要从AppClassLoader 去加载。

说白了,就是 直接让自定义加载器去直接加载Artisan.class 而不让它取委托父加载器去加载,不要去走双亲委派那一套。

我们知道 双亲委派的机制是在ClassLoader # loadClass方法中实现的,打破双亲委派,那我们是不是可以考虑从这个地方下手呢?


如何打破双亲委派

核心: 重写ClassLoader#loadClass方法


演示

刚才的思路是对的,要打破它,那就搞loadClass方法。

重写loadClass方法呗。

我们基于 JVM – 自定义类加载器 再来搞一搞

需要再此基础上 重写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) { 
   
                     
                }

                if (c == null) { 
   
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    
                    //调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类
                    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 不就好了?

我们把loadClass方法的源码copy过来 把双亲委派的部分代码去掉吧,走 改下

重写 ClassLoader#loadClass


public class MyClassLoaderTest { 
   

    static class MyClassLoader extends ClassLoader { 
   
        private String classPath;

        public MyClassLoader(String classPath) { 
   
            this.classPath = classPath;
        }

        private byte[] loadByte(String name) throws Exception { 
   
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name
                    + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }



        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) { 
   
                    c = findClass(name);
                }
                if (resolve) { 
   
                    resolveClass(c);
                }
                return c;
            }
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException { 
   
            try { 
   
                byte[] data = loadByte(name);
                //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) { 
   
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }
    }

重点

   protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException
        { 
   
            synchronized (getClassLoadingLock(name)) { 
   
                // 尝试加载,不存在直接去findClass ,不走委托父类
                Class<?> c = findLoadedClass(name);
                if (c == null) { 
   
                    c = findClass(name);
                }
                if (resolve) { 
   
                    resolveClass(c);
                }
                return c;
            }
        }

运行下

在这里插入图片描述


失败原因探究

略微尴尬, Object.class 找不到 。 为啥 呢? 你加载Boss1的时候, Boss1的父类也需要被加载, 你又把双亲委派给关了, 这个自定义的加载器在本地路径下是找不到Object.class的 。

咋办? 放到自定义的加载器加载的路径下 ?

—–> 其实是不行的,Object 谁能篡改的了啊 ,Object只能由引导类加载器来加载。

临时解决办法

所以换个思路 ,自己的类路径下的对象走我自己的classLoader, 其他的类 还是走双亲委派

 if ("com.gof.facadePattern.Boss1".equals(name)){ 
   
      c = findClass(name);
 }else{ 
   
     // 交由父加载器去加载
      c = this.getParent().loadClass(name);
 }

验证是否成功

这个时候我们在AppClassLoader加载的路径下 再创建个Boss1 (如果走的还是双亲委派,那加载器肯定还是AppClassLoader)

看 是不是这个Boss1 还是被自定义的ClassLoader加载,如果是,说明打破成功。

应用下新建Boss1类

自定义加载路径D:/artisan/com/gof/facadePattern下保留Boss1.class

验证

**加粗样式**

输出结果

在这里插入图片描述

OK,双亲委派机制 打破成功。

这个在tomcat类加载机制中非常重要,所以需要彻底明白这一点。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/126592.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 软件开发与软件研发的区别「建议收藏」

    软件开发与软件研发的区别「建议收藏」按:这几天我一直在写这篇东西,本来是胸有成竹,没想到后来越写越发现自己在这个题目下有太多话想说,而以我现在的能力又不能很好地概括总结,以至于越写越长,文章结构也变得混乱,到后来修改的时候每次都要考虑好久才能下笔,所以决定拆成两部分来发,以便阅读。这篇写得我心力交瘁,质量不算好,凑合着看吧。同样是写程序,不同的岗位工作内容不一样,对程序质量以及工程师的要求也不一样。程序开发大概可以划分成两类…

    2022年7月16日
    25
  • stm32f103可以驱动摄像头吗?_stm32f103c8t6手册

    stm32f103可以驱动摄像头吗?_stm32f103c8t6手册最近,由于想要做摄像头巡线小车,所以就花了两个星期的时间写了一个OV7725的摄像头驱动。主要器材:鹰眼OV7725摄像头、stm32f103vet6、LCD液晶屏(ILI9341)在这里我不讲解OV7725的工作原理(传输时序、寄存器配置),但是关键还是在OV7725上,详细讲解网上有很多,也可以参考下这篇博客https://www.cnblogs.com/raymon-tec/cate…

    2022年9月23日
    0
  • ideal 2021.2 永久激活(已测有效)

    ideal 2021.2 永久激活(已测有效),https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月14日
    385
  • 阿里云和腾讯云免费SSL证书 – 知识林[通俗易懂]

    阿里云和腾讯云免费SSL证书 – 知识林[通俗易懂]本文章来自【知识林】概述什么是SSL证书通俗的来讲SSL和TSL都是属于网络传输的安全协议,而SSL继承于TSL,且SSL是一种更为安全的加密协议。SSL和TSL的体现:TSL是通过浏览器以http://来访问,默认端口是80;SSL是通过浏览器以https://来访问,默认端口是443。 为什么要使用SSL SSL更加安全 在使用微信小程序开发时与后台数据交互必须使用https传输,即SSL协议 SSL如何获得 SSL证书在很多网站上都有出售,且价格都不便

    2022年9月8日
    1
  • Centos7安装arpspoof

    Centos7安装arpspoofLinux安装arpspoof进行arp攻击实验注:仅可用于学习arp原理及攻击防护,切勿用于非法用途!!!环境:centos7一.下面是需要的所有依赖包,yum源都有,只有dsniff和libnids需要另外下载,直接yum安装就可以:yum-yinstalllibICElibSMlibXmulibpcaplibnetlibXextlibXext-develli…

    2022年10月7日
    0
  • 前端聊天功能如何实现_react使用websocket

    前端聊天功能如何实现_react使用websocketchat-room代码已经上传到GitHub,如果喜欢,不妨给一个⭐️说明本项目灵感来自交大x字节跳动的公开课,样式参考其demo1,但本项目采用React2所写,UI组件使用Antd3本项目实现的功能有:用户登录用户注册单人聊天多人聊天表情发送文件传输发送语音视频通话本项目采用的技术有:React&Antd开发前端界面Electron打包应用程序,本地测试不适用于https,因为证书不被信任nodejs&

    2022年10月7日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号