JVM-围观如何打破双亲委派机制

news2024/11/15 18:03:22

下面手把手用代码来实现打破JVM的双亲委派机制

一、Tomcat打破双亲委派机制

以Tomcat类加载为例,Tomcat作为web容器, 那么它要解决什么问题呢?Tomcat 如果使用默认的双亲委派类加载机制行不行?

1.1 Tomcat作为web容器, 那么它要解决什么问题呢?

  • 1.1.1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。

  • 1.1.2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机。

  • 1.1.3. web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。

  • 1.1.4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。

1.2 Tomcat 如果使用默认的双亲委派类加载机制行不行?

答案是不行的。为什么?

  • 第1.1.1的问题,如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认的类加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份。

  • 第1.1.2问题,默认的类加载器是能够实现的,因为他的职责就是保证唯一性

  • 第1.1.3和1.1.4,我们想我们要怎么实现jsp文件的热加载,jsp 文件其实也就是class文件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。

二、Tomcat自定义加载器详解

2.1 tomcat的主要类加载器

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;

  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;

  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;

  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本,这样实现就能加载各自的spring版本;

从图中的委派关系中可以看出:

  • CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离。

  • WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。

  • 而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的热加载功能。

tomcat 这种类加载机制违背了java 推荐的双亲委派模型了吗?

答案是:违背了。 很显然,tomcat 不是这样实现,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委派机制

2.2 模拟Tomcat实现打破双亲委派机制

模拟实现Tomcat的webappClassLoader加载自己war包应用内不同版本类实现相互共存与隔离

package cn.phlos.csdn.demo;

import java.io.FileInputStream;
import java.lang.reflect.Method;

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<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

        /**
         * 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
         *
         * @param name
         * @param resolve
         * @return
         * @throws ClassNotFoundException
         */
        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) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t = System.nanoTime();

                    //非自定义的类还是走双亲委派加载
                    if (!name.startsWith("cn.phlos.csdn.demo")) {
                        c = this.getParent().loadClass(name);
                    } else {
                        c = findClass(name);
                    }

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    }

    public static void main(String args[]) throws Exception {
        MyClassLoader classLoader = new MyClassLoader("D:/test/2023/demo/");
        Class clazz = classLoader.loadClass("cn.phlos.csdn.demo.MyTest");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader());

        System.out.println();
        MyClassLoader classLoader1 = new MyClassLoader("D:/test/2023/demo1/");
        Class clazz1 = classLoader1.loadClass("cn.phlos.csdn.demo.MyTest");
        Object obj1 = clazz1.newInstance();
        Method method1 = clazz1.getDeclaredMethod("sout", null);
        method1.invoke(obj1, null);
        System.out.println(clazz1.getClassLoader());
    }
}

注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。

代码运行步骤:

第一步下载MyTest类,以准备好,下载地址:点击下载-2023.zip

第二步把下载好的压缩包进行解压,修改代码里的D:/test为你解压的路径

第三步,运行上面的代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技云报道:CES 2023:自动驾驶、元宇宙备受瞩目

科技云报道原创。 在被疫情扰乱了两年之后&#xff0c;2023年CES终于回归正常了。 1月5日-8日&#xff0c;CES 2023在拉斯维加斯举行。作为当前全球规模最大、影响力最广泛的消费类电子技术年展&#xff0c;本届展会涵盖的重要主题包括可持续性、数字健康、Web3与元宇宙以及人…

教你如何快速使用店铺管理系统

一、客户管理1、客户资料客户资料报表展示所有新客的数据信息&#xff0c;在此报表可进行会员开卡及新增客户操作&#xff1b;提交会员开卡表单之后&#xff0c;由数据助手更新该客户的会员卡信息至客户信息表单&#xff1b;点击新增客户弹出客户信息表单&#xff0c;默认客户分…

python自学之《21天学通Python》(8)

第11章 文件与文件系统 编写程序来解决实际项目时&#xff0c;很多时候都离不开文件和文件系统的操作。程序本身就是保存在文件系统的文件中的。文件既可以保存程序代码&#xff0c;也可以用来保存各种输入与输出数据。文件和文件系统的处理是任何高级程序设计语言必不可少的一…

Node.js教程笔记(一)

学习目标 1、初识Nodejs 2、fs文件系统模块 3、path路径模块 4、http模块 一、初识Node.js 1.1 浏览器中的JavaScript的运行环境 1、浏览器中的JavaScript的组成部分 2、为什么JavaScript可以在浏览器中被执行&#xff1f; 3、为什么JavaScript可以操作Dom和Bom&#xff…

智公网:公务员的专业限制有哪些?

公务员报考的要求越来越高了&#xff0c;尤其是限制专业这一块&#xff0c;直接阻断了很多人想要跨专业的想法。但是没有办法专业这个条件是硬性的&#xff0c;所以在报考之前一定要看清报考要求。 公务员性质也是比较特殊的&#xff0c;像是一些比较热门专业&#xff0c;在公…

线程安全☞原子性

何为原子性? 原子性&#xff1a;一条线程在执行一系列程序指令操作时&#xff0c;该线程不可中断。一旦出现中断&#xff0c;那么就可能会导致程序执行前后的结果不一致。与数据库中的原子性&#xff08;事务管理体现&#xff09;是相同的 概括&#xff1a;一段程序只能由一条…

Go语言设计与实现 -- 浅谈垃圾回收机制

概述 GC就是垃圾回收机制。而我们知道&#xff0c;内存区域是分成几个块儿的&#xff0c;例如&#xff1a; 堆区&#xff1a;为对象分配内存空间&#xff0c;在栈区和bss区之间存放函数参数&#xff0c;返回值&#xff0c;局部变量全局区&#xff1a;常量区&#xff08;const…

第一批“阳康”涌向三亚,最大的赢家或是携程

在2022年接近尾声时&#xff0c;给居民出行带来近三年困扰的疫情防控&#xff0c;终于迎来了好消息。随着国家“新十条”防控新政策的出台以及优化&#xff0c;过去出门不便、频繁查验核酸码的时代一去不返。尤其是各地逐步放宽出入境管控政策后&#xff0c;在线旅游行业也迎来…

PMP的价值有哪些?

我个人认为&#xff0c;考证只有两个出发点是正确的。一是为了提升自己或者满足自己的兴趣&#xff0c;另一个是和自己的职业规划相关。 比如&#xff0c;有同学想提升自己英语能力&#xff0c;可以考四六级&#xff0c;或者更厉害一点的考雅思、托福。比如&#xff0c;有的同…

电脑自动关机是什么原因?4个解决方法,赶紧码住收藏!

正在使用电脑&#xff0c;突然自动关机。如果没有及时保存好资料&#xff0c;我们辛辛苦苦写的资料就会付诸东流。电脑自动关机是什么原因&#xff1f;其实主要是以下这4个方面的原因。你可以根据下面不同的原因来对症下药&#xff0c;寻找解决电脑自动关机的最好方法&#xff…

直播弹幕系统(七)- 利用动态创建队列完成直播间独立聊天

直播弹幕系统&#xff08;七&#xff09;- 利用动态创建队列完成直播间独立聊天前言一. 动态创建队列1.1 测试 - 动态创建队列1.2 测试 - 聊天室独立前言 上一篇 SpringBoot STOMP RabbitMQ&#xff08;使用MQ替代Spring代理&#xff09; 中主要讲解了如何整合STOMP以及Rabb…

Vue条件语句中v-if、v-else、v-else-if的用法

文章目录1、v-if和v-else结合使用1.1 出现的错误2、v-if、v-else-if和v-else的联合使用2.1 出现的错误3、如果想要同时切换多个元素3.1 效果展示1、v-if和v-else结合使用 v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面&#xff0c;否则它将不会被识别。 <span…

智能指针(二) shared_ptr 注意点

智能指针(二) shared_ptr 注意点 1 不存在 int * 到 shared_ptr 的隐式类型转换 void proc(shared_ptr<int> ptr) {cout << "ptr.use_count()" << ptr.use_count() << endl;cout << "调用成功" << endl;return; }in…

独立产品灵感周刊 DecoHack #043 - 互联网从业者的灵感数据库

本周刊记录有趣好玩的独立产品设计开发相关内容&#xff0c;每周发布&#xff0c;往期内容同样精彩&#xff0c;感兴趣的伙伴可以点击订阅我的周刊。为保证每期都能收到&#xff0c;建议邮件订阅。欢迎通过 Twitter 私信推荐或投稿。很完美的断更了2期&#xff0c;有一期是因为…

RFID技术和NFC技术的原理及区别,你都了解吗?

物联网是信息技术发展的重要推动力&#xff0c;推动了农业、工业、制造业、服务业等多个行业的发展&#xff0c;物联网主要由三个关键技术组成&#xff0c;即连接、物体标识和数据传输&#xff0c;人们把RFID技术作为物体标识的代表&#xff0c;随着技术的进步起源于RFID技术的…

谷粒商城-基础篇-Day07-品牌分类关联与级联更新

将品牌分类和品牌名称的关系放在pms_category_brand_relation表中 获取该列表品牌所有的关联信息 /*** 列表*/GetMapping("/catelog/list")public R list(RequestParam("brandId") Long brandId){List<CategoryBrandRelationEntity> datacategoryBra…

Java日期时间类

Java日期时间类Datenew Date()**获取当前系统时间**通过**指定毫秒数得到时间**format**指定日期格式**SimpleDateFormat的模式字母&#xff1a;parse()可以把**格式化的String转成对应Date**Calendar&#xff08;日历&#xff09;创建日期类对象获取日历对象的某个日历字段第三…

【Linux】五、Linux 进程控制(总)|进程创建|进程终止|进程等待进程程序替换|模拟shell

目录 一、进程创建 1.1 再谈 fork 函数 1.2 fork 函数返回值问题 1.2 写时拷贝 1.3 fork 常规用法 1.4 fork调用失败的原因 二、进程终止 2.1 进程退出码 2.2 进程退出场景 2.3 进程如何退出 三、进程等待 3.1 进程等待必要性 3.2 进程等待的方法 3.2.1 通过 wai…

【二进制安全面试题】linux篇:保护机制、函数调用约定

前言 上来先道歉&#xff0c;对不起(&#xff1e;人&#xff1c;&#xff1b;)对不起&#xff0c;博客鸽了好久。私下有好多朋友问我毕业工作的事情&#xff0c;毕竟搞二进制最重要的是要有热情&#xff01;我能做的也是有限&#xff0c;每个人的学习方式不完全相同&#xff0c…

Http4s 存在输入验证不当漏洞(CVE-2023-22465)

漏洞描述 http4s 是一个用于处理 HTTP 服务的 Scala 接口。 http4s 的受影响版本延迟加载模型化标头&#xff08;modeled headers&#xff09;&#xff0c;用于处理规范化标头的请求&#xff08;如&#xff1a;Option[Header] req.headers.get(“User-Agent”.ci)&#xff0…