【tcomat】聊聊tomcat是如何打破双亲委派模型进行类加载的

双亲委派模型

对于JVM类加载器来说,其实就是如下的code,JDK提供的三个类加载器,每个类加载器都加载自己范围内的类。Boot\EXT\APP 三个。双亲委派一句话就是,先让老爸处理,老爸处理不了,给爷爷。爷爷处理不了,自己在处理。

这样其实就会出现一个问题,越是基础的类,都有父类进行加载,出现两个java.lang.Object类,只会加载核心包的。不会加载用户自己的类。
在这里插入图片描述

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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

tomcat如何设计类加载器

对于tomcat来说,可能有几个方面

  • tomcat运行两个web应用,当有相同的包名+类名,应该需要隔离。
  • 不同的Web应用程序,当出现相同的依赖包,应该统一加载。
  • 隔离tomcat 和 Web应用本身的类

好了带着这三个问题,来思考看tomcat是如何解决的

WebAppClassLoader

第一个问题:当在tomcat下部署多个应用时,同时出现. com.qxlx.StudentServlet的类,应该进行区分,对于类加载器来说不同的类加载器加载相同的全限定类名认为是不同的类,所以针对每个web应用创建一个单独的classLoader。也即WebAppClassLoader,每个Web应用有自己的类空间,Web应用之间可以通过各自的类加载器进行隔离。

SharedClassLoader

第二个问题:多个Web应用之间怎么进行共享相同的库,对于双亲委派模型来说,就是通过父类进行加载最基础的类,因此只需要在webAppClassLoader进行设置一个父类加载器,SharedClassLoader 专门加载Web应用之间共享的类,当WebAppClassLoader没有加载这个类,就委托给SharedClassLoader去加载。

catalinaClassLoader

第三个问题:也就是web应用和tomcat进行隔离,我们知道共享需要继承,隔离就需要兄弟关系。因此,需要在SharedClassLoader 创建一个兄弟类加载器,CatalinaClassLoader。加载tomcat基础类,但是这样有问题,就是tomcat和web应用共享的类如何加载。

其实就是在加一层父类加载器,CommonClassLoader
CatalinaClassLoader加载基础的类,因此最终就是如下图。
在这里插入图片描述

那么tomcat是如何进行设置类加载器的,源码之下无秘密。可以看到,先创建一个common,然后catalina和shared 并设置父类加载器为common.

    private void initClassLoaders() {try {//创建common类加载器commonLoader = createClassLoader("common", null);if (commonLoader == null) {// no config file, default to this loader - we might be in a 'single' env.commonLoader = this.getClass().getClassLoader();}// 创建catalinaLoader,父类加载器为commonLoadercatalinaLoader = createClassLoader("server", commonLoader);// 创建sharedLoader,父类加载器为commonLoadersharedLoader = createClassLoader("shared", commonLoader);} catch (Throwable t) {handleThrowable(t);log.error("Class loader creation threw exception", t);System.exit(1);}}

并且设置为当前线程类加载器为catalinaLoader。由于bootStrap调用的是catalina的init 和 start() ,通过反射常见catalina对象,并设置父类加载器为 shardloader

	// 设置上下文类加载器 catalinaLoader  加载tomcat专用的类Thread.currentThread().setContextClassLoader(catalinaLoader);// catalinaLoader 加载 Catalina 类Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");Object startupInstance = startupClass.getConstructor().newInstance();String methodName = "setParentClassLoader";Class<?> paramTypes[] = new Class[1];paramTypes[0] = Class.forName("java.lang.ClassLoader");Object paramValues[] = new Object[1];paramValues[0] = sharedLoader;// 设置catalina类的parentClassLoader属性为shardloaderMethod method =startupInstance.getClass().getMethod(methodName, paramTypes);method.invoke(startupInstance, paramValues);

每个web应用其实就是一个context , context的实现StandardContext 可以发现为每个web应用创建一个单独的类加载器,这样就可以加载了。

    // 为每个应用设置类加载器if (getLoader() == null) {WebappLoader webappLoader = new WebappLoader();webappLoader.setDelegate(getDelegate());setLoader(webappLoader);}

源码

public Class<?> findClass(String name) throws ClassNotFoundException {...Class<?> clazz = null;try {//1. 先在Web应用目录下查找类 clazz = findClassInternal(name);}  catch (RuntimeException e) {throw e;}if (clazz == null) {try {//2. 如果在本地目录没有找到,交给父加载器去查找clazz = super.findClass(name);}  catch (RuntimeException e) {throw e;}//3. 如果父类也没找到,抛出ClassNotFoundExceptionif (clazz == null) {throw new ClassNotFoundException(name);}return clazz;
}
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {Class<?> clazz = null;//1. 先在本地cache查找该类是否已经加载过clazz = findLoadedClass0(name);if (clazz != null) {if (resolve)resolveClass(clazz);return clazz;}//2. 从系统类加载器的cache中查找是否加载过clazz = findLoadedClass(name);if (clazz != null) {if (resolve)resolveClass(clazz);return clazz;}// 3. 尝试用ExtClassLoader类加载器类加载,为什么?ClassLoader javaseLoader = getJavaseClassLoader();try {clazz = javaseLoader.loadClass(name);if (clazz != null) {if (resolve)resolveClass(clazz);return clazz;}} catch (ClassNotFoundException e) {// Ignore}// 4. 尝试在本地目录搜索class并加载try {clazz = findClass(name);if (clazz != null) {if (resolve)resolveClass(clazz);return clazz;}} catch (ClassNotFoundException e) {// Ignore}// 5. 尝试用系统类加载器(也就是AppClassLoader)来加载try {clazz = Class.forName(name, false, parent);if (clazz != null) {if (resolve)resolveClass(clazz);return clazz;}} catch (ClassNotFoundException e) {// Ignore}}//6. 上述过程都加载失败,抛出异常throw new ClassNotFoundException(name);
}

在这里插入图片描述
核心流程,其实就是先查找加载过没有,没有加载过,然后先尝试用EXT进行加载,避免覆盖JRE核心类。然后才是本地加载 以及app加载。

小结

本篇主要介绍tomcat是如何打破双亲委派模型,以及相关的源码流程,其实在学习的过程中,源码更多的是是解决问题时的一个落地实现,我们需要掌握背后设计的思路更关键。

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

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

相关文章

x86 的 ebp 寄存器,可能比 cr3 更重要,好好掰扯一下 ebp

在 x86 架构的计算机中&#xff0c;ebp&#xff08;Extended Base Pointer&#xff09;寄存器通常用于指向当前函数的栈帧&#xff08;stack frame&#xff09;的基地址。栈帧是函数调用期间在栈上分配的一块内存区域&#xff0c;用于存储局部变量、函数参数、返回地址和其他临…

视图(views)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 下面通过一个例子讲解在Django项目中定义视图&#xff0c;代码如下&#xff1a; from django.http import HttpResponse # 导入响应对象 impo…

TOPGP-TIPTOP调用外部Webservice

功能要求&#xff1a;ERP作业调用外部系统的webserice更新数据。 演示环境&#xff1a;ERP作业cooi002&#xff08;员工档案&#xff09;录入后更新到外部系统员工档案表。 1、外部系统的WebSerice使用.net搭建 2、在Service.cs中写一个调用方法erp_other erp_other中两个参数…

Bootstrap和Bagging算法以及衍生算法

1. Bootstrap算法 实际上就是一种针对小样本的无放回式的抽样方法&#xff0c;通过方差的估计可以构造置信区间。 其核心思想和基本步骤如下&#xff1a;   &#xff08;1&#xff09; 采用重抽样技术从原始样本中抽取一定数量&#xff08;自己给定&#xff09;的样本&#…

Android集成高德地图SDK(2)

1.解压下载的压缩包&#xff0c;找到AMap_Android_SDK_All\AMap3DMap_DemoDocs\AMap_Android_API_3DMap_Demo\AMap3DDemo\app\libs&#xff0c;复制libs里的所有文件&#xff0c;将其粘贴到Android工程的libs目录下&#xff0c;如图所示。 2.打开app下的build.gradle&#xff0…

记录react实现选择框一二级联动出现的问题

需求&#xff1a;用户在选择第一个选择框的选项后&#xff0c;第二个选择框的选项会根据第一个选择框的选择动态更新。如图所示 出现的问题 一级分类选择之后二级分类没有数据&#xff0c;第二次重新选择一级分类的时候&#xff0c;二级分类就会有值。 第一次点击截图&#x…

024.两两交换链表中的节点,用递归和 while 循环

题意 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 难度 中等 示例 输入&#xff1a;head [1,2,3,4] 输出&#xff1a;[…

什么是车载测试?车载测试怎么学!

1、车载测试是什么&#xff1f; 车载测试分很多种&#xff0c;有软件测试、硬件测试、性能测试、功能测试等等&#xff0c;每一项测试的内容都不一样&#xff0c;我们所说的车载测试主要指的是汽车软件的功能测试&#xff0c;也就是针对汽车实现的某一个功能&#xff0c;而进行…

vue3 vxe-grid列中绑定vxe-switch实现数据更新

1、先上一张图&#xff1a; <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…

Trilium windows上修改笔记目录,创建多个笔记空间方法

一开始使用trilium会非常的不舒服&#xff0c;不像是obsidian可以创建多个笔记空间&#xff0c;指定多个笔记目录。这里摸索到了解决方案 修改目录的方法一 ——修改系统环境变量 打开控制面板-系统-高级系统设置 新增如上条目 修改目录的方法二——直接写bat脚本运行 新建位…

网安大咖说·镜鉴(下)| 把握安全新脉搏:企业CSO的领航之道

网安大咖说镜鉴栏目通过对网安大咖说嘉宾访谈内容的深度提炼&#xff0c;撷取群英论道之精髓&#xff0c;汇聚众智谋策之高远&#xff0c;为从业者提供宝贵的经验和启迪。集思广益、博采众长&#xff0c;意在以镜为鉴&#xff0c;观网安之百态&#xff0c;立防范之策略&#xf…

AI助力科研:自动化科学构思生成系统初探

科学研究作为推动创新和知识进步的关键活动&#xff0c;在解决复杂问题和提升人类生活水平方面发挥着至关重要的作用。然而&#xff0c;科学研究的固有复杂性、缓慢的进展速度以及对专业专家的需求&#xff0c;限制了其生产力的提升。为了增强科研效率&#xff0c;本文提出了一…

重学java 84.Java枚举

那些你暗自努力的时光&#xff0c;终究会照亮你前行的路 —— 24.6.24 一、枚举介绍&#xff08;开发中表示状态&#xff09; 1.概述&#xff1a; 五大引用数据类型&#xff1a;类型、数组、接口、注解、枚举 2.定义&#xff1a; public enum 枚举类名{} 所有的枚举类父类…

贝锐花生壳内网穿透

贝锐花生壳内网穿透使用步骤 首先你得去官网购买一个域名配置一下内网穿透映射官网下载一个客户端修改代码配置 首先你得去官网购买一个域名 配置一下内网穿透映射 官网下载一个客户端 注意&#xff0c;一定要下载客户端&#xff0c;不然用不了 当然&#xff0c;本地我已经提前…

SpringBoot-配置文件中使用随机值和使用变量

1、配置文件中使用随机值 2.在配置文件使用引用变量 如果没定义还可以设置默认值

环境安装-GIT

下载 git官网下载 https://git-scm.com/ 安装 点击下载的安装包&#xff0c;并点击下一步 选择安装路径&#xff0c;照例改选自定义路径 选择默认的即可 选择GIT编辑器&#xff0c;默认选择vim即可 设置初始化新项目(本地仓库)的主分支名&#xff0c;按默认即可&#xff0c;点…

keysight 34901A (安捷伦)多路复用器

34970A 数据采集/开关单元的 Keysight 34901A&#xff08;安捷伦&#xff09;模块是通用扫描中最通用的多路复用器。它将密集的多功能开关与 60 通道/秒的扫描速率相结合&#xff0c;可满足广泛的数据采集应用。两线和四线通道可以混合在同一模块上。两个额外的保险丝输入&…

音频傅里叶变换(基于开源kissffs)

主要参考资料&#xff1a; 深入浅出的讲解傅里叶变换&#xff08;真正的通俗易懂&#xff09;: https://zhuanlan.zhihu.com/p/19763358 推荐开源项目&#xff1a;KISS FFT&#xff1a; https://blog.csdn.net/gitblog_00031/article/details/138840117 数字硅麦数据的处理&…

基于Java蛋糕甜品商城系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

LLama 3的各种微调:拿我司七月的paper-review数据集微调LLama 3

前言 llama 3出来后&#xff0c;为了通过paper-review的数据集微调3&#xff0c;有以下各种方式 不用任何框架 工具 技术&#xff0c;直接微调原生的llama 3&#xff0c;毕竟也有8k长度了 效果不期望有多高&#xff0c;纯作为baseline通过PI&#xff0c;把llama 3的8K长度扩展…