Tomcat如何打破双亲委托机制?

protected final Class<?> defineClass(byte[] b, int off, int len){

}

}

JVM的类加载器是分层的父子关系,每个类加载器都持有一个parent字段指向父加载器。

  • defineClass 工具方法:调用native方法把Java类的字节码解析成一个Class对象

  • findClass 就是找到 .class 文件,可能来自文件系统或网络,找到后把 .class 文件读到内存得到字节码数组,然后调用defineClass方法得到Class对象

loadClass 首先检查这个类是不是已经被加载过了,如果加载过了直接返回,否则交给父加载器去加载。

这是个递归调用,即子加载器持有父加载器引用,当一个类加载器需加载一个Java类时,会先委托父加载器去加载,然后父加载器在自己加载路径中搜索Java类,当父加载器在自己的加载范围内找不到时,才会交还给子加载器加载,这就是双亲委托机制。

JDK的类加载器工作原理是一样的,区别只是加载路径不同,即findClass查找的路径不同。

双亲委托机制是为保证一个Java类在JVM的唯一性。假如你手滑写个与JRE核心类同名类,比如Object,双亲委托机制能保证加载的是JRE里的那个Object类,而不是你写的Object。

因为AppClassLoader在加载你的Object类时,会委托给ExtClassLoader去加载,而ExtClassLoader又会委托给BootstrapClassLoader,BootstrapClassLoader发现自己已经加载过了Object类,会直接返回,不会去加载你的Object类。

类加载器的父子关系不是通过继承来实现的,比如AppClassLoader并非ExtClassLoader的子类,只是AppClassLoader的parent指向ExtClassLoader对象。

所以若自定义类加载器,不是去继承AppClassLoader,而是继承ClassLoader抽象类,再重写findClass和loadClass即可。

Tomcat就是通过自定义类加载器实现自己的类加载。

若你要打破双亲委托,也就只需重写loadClass,因为loadClass的默认实现就是双亲委托机制。

Tomcat的类加载器

==========================================================================

Tomcat的自定义类加载器WebAppClassLoader打破了双亲委托机制:

首先自己尝试去加载某个类,如果找不到再委托给父类加载器,目的是优先加载Web应用自己定义的类。

只需重写ClassLoader的两个方法:

findClass


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. 如果父类也没找到,抛出ClassNotFoundException

if (clazz == null) {

throw new ClassNotFoundException(name);

}

return clazz;

}

工作流程

  • 先在Web应用本地目录下查找要加载的类

  • 若未找到,交给父加载器查找,即AppClassLoader

  • 若父加载器也没找到这个类,抛ClassNotFound

loadClass


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);

}

工作流程

  • 先在本地Cache查找该类是否已加载过

即Tomcat的类加载器是否已经加载过这个类。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
-fzVhUrj0-1715100445032)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值