当前位置: 首页 > news >正文

easyui datagrid重新加载detailview异常_tomcat类加载机制了解一下

一、tomcat类加载器关系

49b0b72d2b34f0fb967f9fcd835a0619.png

    tomcat类加载器设计结构如上图所示,上面三个Bootstrap加载器、Ext加载器、Application加载器是JVM加载器,下半部分的加载器才是tomcat自定义的加载器。

    学习tomcat类加载器之前先回顾下之前写过的jvm类加载器的知识点。

·  类加载机制

    类加载机制的本质是将经过虚拟机编译后的.class文件转化为二进制,并将二进制加载成Class对象的加载过程。

    因此判断类是否完全相同,需要以下两个条件即:类的全路径名是否相同、加载类的类加载器是否为同一个,这两个条件决定类是否完全相同。

·  双亲委派机制

    双亲委派机制的存在,保证类是安全的,他的存在保证了类的加载顺序会优先委派父类加载器进行加载,如果父类加载器没有加载才会有子类进行加载。比如在JVM类加载器的继承关系(从子到父)为Application ClassLoader—>Extension ClassLoader—>Bootstrap ClassLoader。因此当自己在代码内部中编写恶意java代码时,比如HashMap文件,如果路径名相同,则会委派给父类Bootstrap ClassLoader进行加载,发现在Bootstrap加载的lib目录下存在HashMap文件则加载应该由Bootstrap加载的HashMap文件。从而保证自己编写的恶意java代码不会替换应由Bootstrap加载的源文件。

·  打破双亲委派机制

    由于虚拟机中的加载规则是按需加载的,即需要用到什么类的时候才会去加载那个类。并且在加载该类时用的是什么加载器,那么加载该类引用的类也需要用到对应的加载器,在java中的SPI机制,加载jdbc时由于Driver类不在rt.jar中因此不能被Bootstrap加载器进行加载,因此使用了线程上下文类加载器委派子类进行加载。所以打破了双亲委派机制,并且在tomcat类加载器中也存在打破双亲委派机制的情况。

二、tomcat类加载器

·  Common ClassLoader

    Common ClassLoader是tomcat最基本的类加载器,被此加载器加载的类即可以被tomcat所访问,也可以被应用war包中的程序所访问。

·  Catalina ClassLoader

    Catalina ClassLoader是tomcat私有的类加载器,被此加载器加载的类,只能被tomcat所访问。

·  Shared ClassLoader

    Shared ClassLoader是各个war包共享的类加载器,被此加载器加载的类,只能被应用war包中的程序所访问。

    当tomcat中存在多个war包并同时使用了相同版本的jar包时,为了减少资源的浪费,可以使用该加载器,抽出这些相同版本的jar包,使用Shared ClassLoader加载一次被共享的jar即可,来代替每个war包中都需要加载的过程。

·  WebApp ClassLoader

    WebApp ClassLoader是多个war包的类加载器,即tomcat中的一个war包由一个WebApp ClassLoader加载。

    Shared ClassLoader和WebApp ClassLoader的存在可以让共享的jar包由Shared ClassLoader加载,其余不一样或需要独立部署的由WebApp ClassLoader进行加载,达成共享与隔离的统一。

·  JSP ClassLoader

    JSP ClassLoader是一个jsp文件的类加载器,即tomcat中存在一个jsp就会new出一个JSP ClassLoader进行jsp文件的加载。

    JSP ClassLoader是一个特殊的类加载器,他的特点也保证了jsp文件可以进行热部署,因为当对jsp文件进行修改,重新加载该jsp文件时,又会new出一个JSP ClassLoader来加载jsp文件,替换修改前的jsp文件(此时需要注意的是jsp文件的类的唯一性也变了,类的唯一性由类的全路径名+类加载器决定,此时的类加载器是重新new出来的所以和修改前的加载器是完全不一样的)。而Controller、service等文件的修改前和修改后是由相同的WebApp ClassLoader加载的,因此不能在这种情况下和jsp一样实现实现修改后的热部署。

·  源码中的加载器关系

private void initClassLoaders() {try {//common加载器,父类为null        commonLoader = createClassLoader("common", null);        if (commonLoader == null) {
commonLoader = this.getClass().getClassLoader(); }//catalina加载器,父类为commonLoader catalinaLoader = createClassLoader("server", commonLoader); //shared加载器,父类为commonLoader sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) {
handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); }
}

    但是创建common加载器时,传入的是null,如何判断common加载器的父类是ApplicationClassLoader呢。

81012f8f909013d13ee16bf1df95c45e.png

    跟到底层会发现获取系统类加载器为父类加载器,即AppClassLoader。其余源码的分析在类加载详解中有进行过分析,这里不再做详细阐述。

三、tomcat类加载流程

b14c0b323dbff3bd01338d3f691558b1.png

·  Bootstrap

    主要加载JVM启动所需要的类。

·  System

    主要加载tomcat启动的类,比如bootstrap.jar。位于bin/bootstrap.jar。

c373d1f05fde96e57df362d856813c66.png

·  Common

    主要加载tomcat中常用的类,位于lib中。

402c36872a440dde243a715937944480.png

·  WebApp

    主要加载应用的类文件,即位于WEB-INF/lib下的jar文件和WEB-INF/classes下的class文件。

四、tomcat类加载源码分析

public Class> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}//重写了loadClass方法public Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {if (log.isDebugEnabled())
log.debug("loadClass(" + name + ", " + resolve + ")"); Class> clazz = null; //校验程序是否启动,如果已启动抛出异常 checkStateForClassLoading(name); //校验当前对象缓存是否加载过该类 clazz = findLoadedClass0(name); //如果已经加载过该类 则直接返回 if (clazz != null) {if (log.isDebugEnabled())
log.debug(" Returning class from cache"); if (resolve)
resolveClass(clazz); return clazz; }//校验是否加载过该类,会调用native方法 clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name); if (clazz != null) {if (log.isDebugEnabled())
log.debug(" Returning class from cache"); if (resolve)
resolveClass(clazz); return clazz; }
String resourceName = binaryNameToPath(name, false); ClassLoader javaseLoader = getJavaseClassLoader(); boolean tryLoadingFromJavaseLoader; try {//使用javaseLoader加载 // 即-使用ExtClassLoader/BootstrapClassLoader判断是否需要加载这个calss // 防止先被webAppClassLoader加载了脏的类 URL url; if (securityManager != null) {
PrivilegedAction dp = new PrivilegedJavaseGetResource(resourceName); url = AccessController.doPrivileged(dp); } else {
url = javaseLoader.getResource(resourceName); }
tryLoadingFromJavaseLoader = (url != null); } catch (Throwable t) {
ExceptionUtils.handleThrowable(t); tryLoadingFromJavaseLoader = true; }if (tryLoadingFromJavaseLoader) {try {
clazz = javaseLoader.loadClass(name); if (clazz != null) {if (resolve)
resolveClass(clazz); return clazz; }
} catch (ClassNotFoundException e) {
}
}//安全策略校验 if (securityManager != null) {int i = name.lastIndexOf('.'); if (i >= 0) {try {
securityManager.checkPackageAccess(name.substring(0, i)); } catch (SecurityException se) {
String error = sm.getString("webappClassLoader.restrictedPackage", name); log.info(error, se); throw new ClassNotFoundException(error, se); }
}
}//判断是否需要被WebAppClassLoader进行加载,是否设置了delegate boolean delegateLoad = delegate || filter(name, true); //委托给父类 CommonClassLoader加载 if (delegateLoad) {if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader1 " + parent); try {
clazz = Class.forName(name, false, parent); if (clazz != null) {if (log.isDebugEnabled())
log.debug(" Loading class from parent"); if (resolve)//加载成功则返回 resolveClass(clazz); return clazz; }
} catch (ClassNotFoundException e) {
}
}//使用WebAppClassLoader进行加载 if (log.isDebugEnabled())
log.debug(" Searching local repositories"); try {
clazz = findClass(name); if (clazz != null) {if (log.isDebugEnabled())
log.debug(" Loading class from local repository"); if (resolve)
resolveClass(clazz); return clazz; }
} catch (ClassNotFoundException e) {
}//WebAppClassLoader加载失败,则委托WebAppClassLoader的父类进行加载 if (!delegateLoad) {if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader at end: " + parent); try {
clazz = Class.forName(name, false, parent); if (clazz != null) {if (log.isDebugEnabled())
log.debug(" Loading class from parent"); if (resolve)
resolveClass(clazz); return clazz; }
} catch (ClassNotFoundException e) {// Ignore }
}
}//加载不到 抛出异常 throw new ClassNotFoundException(name);}

    上述源码分析中,tomcat的类加载步骤为(delegate设置为true的情况):

(1)判断当前被加载的类对象是否存在缓存中,如果存在则直接返回该类。

(2)判断是否加载过该类,如果加载过则直接返回该类。

(3)委派jvm中的父类ExtClassLoader、BootStrapClassLoader是否加载过该类。

(4)委派tomcat中的父类CommonClassLoader是否加载过该类。

(5)当前WebAppClassLoader自己加载该类。

(6)如果类仍然没有加载成功,则抛出异常。

五、tomcat打破双亲委派加载机制

    不论加载的delegate配置设置为true或者false,tomcat加载类的顺序都为先查询当前WebAppClassLoader类加载器是否加载过该类,而不是预先委托父类进行加载。

    第二点是当delegate设置为false时,上述的第四步和第五步可能也会打破双亲委派,优先使用WebAppClassLoader加载器进行加载,加载不到才委托父类进行加载。

相关文章:

  • 关于python格式对齐的问题_python格式对齐
  • css清除浮动的几种方法_小猿圈之HTML/css清除浮动的方法都有哪些?
  • python初学者编程指南源码_如何系统地自学 Python?
  • sublime编辑python_Sublime配置python开发环境
  • python快速入门naomiceder_零基础小白python入门必看之Cartopy的基础使用
  • 为什么写了value属性 jq赋值value值不显示_Vue计算属性原理和使用场景
  • python什么为假_在Python中使用filter去除列表中值为假及空字符串的例子
  • php不调用父类构造_popchain与对应poc的构造分析
  • 最常用20000英语单词表_高中英语 | 英语单词中最常用的300+个前缀、后缀
  • python类方法继承_Python类继承(调用父类成员与方法)
  • 为什么sublime保存的文件不是html_Web前端零基础到实战全系列之HTML5工具Sublime使用和Web标准...
  • case when 子查询_SQL多表查询
  • rabbitmq入门_SpringBoot整合RabbitMQ(一)快速入门
  • select count(*) from返回的类型_SQL面试题类型整理
  • 用自底向上算法为一组整数构造一个大根堆。_Mathematical Cryptography笔记:整数分解和RSA...
  • axios 和 cookie 的那些事
  • Centos6.8 使用rpm安装mysql5.7
  • Consul Config 使用Git做版本控制的实现
  • Druid 在有赞的实践
  • java 多线程基础, 我觉得还是有必要看看的
  • JavaScript-Array类型
  • JS数组方法汇总
  • SpingCloudBus整合RabbitMQ
  • windows下使用nginx调试简介
  • 模型微调
  • 容器服务kubernetes弹性伸缩高级用法
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 网络应用优化——时延与带宽
  • 异常机制详解
  • 译有关态射的一切
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • postgresql行列转换函数
  • ​人工智能书单(数学基础篇)
  • # Maven错误Error executing Maven
  • (1) caustics\
  • (C#)一个最简单的链表类
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (一)Linux+Windows下安装ffmpeg
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • ****Linux下Mysql的安装和配置
  • .a文件和.so文件
  • .FileZilla的使用和主动模式被动模式介绍
  • .NET CLR Hosting 简介
  • .NET 回调、接口回调、 委托
  • .net(C#)中String.Format如何使用
  • /etc/shadow字段详解
  • [<死锁专题>]
  • [BZOJ] 1001: [BeiJing2006]狼抓兔子
  • [C# 基础知识系列]专题十六:Linq介绍
  • [CareerCup] 6.1 Find Heavy Bottle 寻找重瓶子
  • [Django 0-1] Core.Email 模块
  • [linux] 创建用户