深入拆解Tomcat & Jetty
李号双
eBay技术主管
立即订阅
6067 人已学习
课程目录
已完结 44 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | Java程序员如何快速成长?
免费
模块一 必备基础 (4讲)
01 | Web容器学习路径
02 | HTTP协议必知必会
03 | 你应该知道的Servlet规范和Servlet容器
04 | 实战:纯手工打造和运行一个Servlet
模块二 整体架构 (9讲)
05 | Tomcat系统架构(上): 连接器是如何设计的?
06 | Tomcat系统架构(下):聊聊多层容器的设计
07 | Tomcat如何实现一键式启停?
08 | Tomcat的“高层们”都负责做什么?
09 | 比较:Jetty架构特点之Connector组件
10 | 比较:Jetty架构特点之Handler组件
11 | 总结:从Tomcat和Jetty中提炼组件化设计规范
12 | 实战:优化并提高Tomcat启动速度
13 | 热点问题答疑(1):如何学习源码?
模块三 连接器 (9讲)
14 | NioEndpoint组件:Tomcat如何实现非阻塞I/O?
15 | Nio2Endpoint组件:Tomcat如何实现异步I/O?
16 | AprEndpoint组件:Tomcat APR提高I/O性能的秘密
17 | Executor组件:Tomcat如何扩展Java线程池?
18 | 新特性:Tomcat如何支持WebSocket?
19 | 比较:Jetty的线程策略EatWhatYouKill
20 | 总结:Tomcat和Jetty中的对象池技术
21 | 总结:Tomcat和Jetty的高性能、高并发之道
22 | 热点问题答疑(2):内核如何阻塞与唤醒进程?
模块四 容器 (8讲)
23 | Host容器:Tomcat如何实现热部署和热加载?
24 | Context容器(上):Tomcat如何打破双亲委托机制?
25 | Context容器(中):Tomcat如何隔离Web应用?
26 | Context容器(下):Tomcat如何实现Servlet规范?
27 | 新特性:Tomcat如何支持异步Servlet?
28 | 新特性:Spring Boot如何使用内嵌式的Tomcat和Jetty?
29 | 比较:Jetty如何实现具有上下文信息的责任链?
30 | 热点问题答疑(3):Spring框架中的设计模式
模块五 通用组件 (4讲)
31 | Logger组件:Tomcat的日志框架及实战
32 | Manager组件:Tomcat的Session管理机制解析
33 | Cluster组件:Tomcat的集群通信原理
特别放送 | 如何持续保持对学习的兴趣?
模块六 性能优化 (8讲)
34 | JVM GC原理及调优的基本思路
35 | 如何监控Tomcat的性能?
36 | Tomcat I/O和线程池的并发调优
37 | Tomcat内存溢出的原因分析及调优
38 | Tomcat拒绝连接原因分析及网络优化
39 | Tomcat进程占用CPU过高怎么办?
40 | 谈谈Jetty性能调优的思路
41 | 热点问题答疑(4): Tomcat和Jetty有哪些不同?
结束语 (1讲)
结束语 | 静下心来,品味经典
深入拆解Tomcat & Jetty
登录|注册

24 | Context容器(上):Tomcat如何打破双亲委托机制?

李号双 2019-07-04
相信我们平时在工作中都遇到过 ClassNotFound 异常,这个异常表示 JVM 在尝试加载某个类的时候失败了。想要解决这个问题,首先你需要知道什么是类加载,JVM 是如何加载类的,以及为什么会出现 ClassNotFound 异常?弄懂上面这些问题之后,我们接着要思考 Tomcat 作为 Web 容器,它是如何加载和管理 Web 应用下的 Servlet 呢?
Tomcat 正是通过 Context 组件来加载管理 Web 应用的,所以今天我会详细分析 Tomcat 的类加载机制。但在这之前,我们有必要预习一下 JVM 的类加载机制,我会先回答一下一开始抛出来的问题,接着再谈谈 Tomcat 的类加载器如何打破 Java 的双亲委托机制。

JVM 的类加载器

Java 的类加载,就是把字节码格式“.class”文件加载到 JVM 的方法区,并在 JVM 的堆区建立一个java.lang.Class对象的实例,用来封装 Java 类相关的数据和方法。那 Class 对象又是什么呢?你可以把它理解成业务类的模板,JVM 根据这个模板来创建具体业务类对象实例。
JVM 并不是在启动时就把所有的“.class”文件都加载一遍,而是程序在运行过程中用到了这个类才去加载。JVM 类加载是由类加载器来完成的,JDK 提供一个抽象类 ClassLoader,这个抽象类中定义了三个关键方法,理解清楚它们的作用和关系非常重要。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Tomcat & Jetty 》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(42)

  • 罗乾林
    我想打破双亲委托机制,能保证不同版本的类共存,就像一个tomcat下多个工程,使用了不同版本的spring,各加载各的互不影响。如果不打破双亲委托机制,都交由AppClassLoader去加载,那么相同包名相同类名的类就被判定已经加载过了,达不到加载不同版本的功能。由于自定义了类加载器,即使包名与类名相同,但类加载器不同依然被判断为不同的类

    作者回复: 对的,下一篇就说隔离

    2019-07-06
    11
  • nightmare
    tomcat的类加载机制老师剖析的很透彻,先扩展类加载器加载,这样避免自己覆盖JRE中的类然后再自定义的加载器加载,最后应用加载器加载,有一个疑问,就是比如我一个tomcat部署了多个web应用,如果都有spring的jar包,由于自定义的类加载器先加载spring的jar包,这样spring的jar在每一个context应用岂不是都要加载一次?如果应用加载器先加载,是不是就可以只加载一次spring的jar,然后自定义的加载器只加载不共用的class?

    作者回复: 你说的很对,Spring的类应该是由sharedclassloader来加载,所以不应该把Spring的包放到Web应用的路径下,应该放到Tomcat指定的共享目录下

    2019-07-04
    8
  • Monday
    1、原文【tomcat 的自定义类加载器 WebAppClassLoader 打破了双亲委托机制,它首先自己尝试去加载某个类,如果找不到再代理给父类加载器,其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader 的两个方法:findClass 和 loadClass。】
    问题:就不怕tomcat类加载器覆盖ExtClassloader加载的类?

    2、原文【从上面的过程我们可以看到,Tomcat 的类加载器打破了双亲委托机制,没有一上来就直接委托给父加载器,而是先在本地目录下加载,为了避免本地目录下的类覆盖 JRE 的核心类,先尝试用 JVM 扩展类加载器 ExtClassLoader 去加载。那为什么不先用系统类加载器 AppClassLoader 去加载?很显然,如果是这样的话,那就变成双亲委托机制了,这就是 Tomcat 类加载器的巧妙之处。】
    问题:后面的回答,感觉像是为了打破而打破似的。

    3、听/看本节多次,最后还是没体悟到Tomcat打破双亲委托模式的好处。。。

    作者回复: 假如web应用目录下有个Spring2.0版本的库,系统class类路径下有个Spring 1.0版本的库,打破双亲委托的好处是web应用会加载web应用路径下那个Spring。

    2019-07-08
    7
  • Liam
    老师能讲下什么是上下文加载器吗,什么情况下会用到它?这个和双亲委派有关吗

    作者回复: 线程上下文加载器其实是线程私有数据,跟线程绑定的属性

    2019-07-04
    1
    5
  • QQ怪
    双亲委派模型其实不是叫单亲委派更好?

    作者回复: 还真是

    2019-07-05
    4
  • 妥协
    看到过这样一句话,一直没想明白:"类的唯一性由类加载器实例和类的全名一同确定的,即使同一串字节流由不同的类加载器加载,也是不同的实例" ,每个类加载器加载前都会判断是否已经加载过,同名的类判断加载过了,不是不会在加载吗?

    作者回复: 每个类记载“实例”只判断自己是否加载过

    2019-07-04
    4
  • 强哥
    每篇文章最后的总结,若能概括出这么做的意图及优点,这样对读者来说收益更大。

    作者回复: Servlet规范建议,全路径类名与系统类同名的话,优先加载web应用自己定义的类。

    2019-07-04
    4
  • Mq
    李老师,为什么要打破双亲委托

    作者回复: 文中其实有解释,servlet规范建议这么做

    2019-07-04
    1
    4
  • Li Shunduo
    在其他地方看到过可以通过配置<Loader delegate="true"/>让tomcat遵循双亲委派,老师在答疑篇可以展开讲讲吗?

    作者回复: 是的,如果你不想Tomcat打破双亲委托,可以配这个参数

    2019-07-10
    3
  • Monday
    如果能画出Tomcat类加载器打破与不打破双亲委托模式对比图,我想比文字更具说明性。
    2019-07-08
    3
  • 新世界
    由于沿用双亲委派重写findClass即可,找不到最后到固定目录下查找,不需要重写loadClass,还有一点不明白,tomcat为什么要打破双亲委派定义自己的classloader,不定义不行吗?

    作者回复: Servlet规定这样做的,优先加载web应用目录下的类,只要这个类不覆盖jre核心类

    2019-07-04
    3
  • 火凤凰
    老师,这边我有个疑问。tomcat 中的findClass方法里面首先是现在本地去查找要加载的类,找不到然后让父加载器去查找。然后在loadClass中,第四步调用findClass要加载的类的时候找不到,然后去调用第五步通过Class.forName()去加载尼?这一步是不是多余的?明明findClass方法应该包含了AppClassLoader需要查找的类吧?还望老师或者一起学习的大佬们解惑一下?

    作者回复: findclass只在本地应用目录下找,不包括AppClassLoader的加载路径

    2019-08-29
    2
  • Mr.差不多
    双亲委派规则是 当父加载器找不到此文件时才交给子加载器去加载。那么我觉得Tomcat重写loadClass方法其实也是这个逻辑。假设现在有一个类是需要在WebAppClassLoader加载的,那么它会先查询是否在AppClassLoader加载过,如果没有那么查看是否在ExtClassLoader加载过,那么这一系列步骤不就是为了保证没有在父加载器找不到此文件吗?这不还是双亲委派的模型吗?麻烦老师给解答下

    作者回复: WebAppClassLoader不会首先委托给AppClassLoader去加载,而是ExtClassLoader。这是根本区别

    2019-07-05
    2
  • 吖蒲
    老师,我看源码了还是有点不懂,第三步骤tomcat的类加载器先用extclassloader加载类,ext让父加载器bootstrap去加载,防止优先加载应用中同名的类,那我用appclassloader,最终也能达到bootstrap加载器去加载类的效果,那为什么不直接调用appclassloader加载??

    作者回复: 好问题,appclassloader会加载PATH目录下的类,这样就达不到优先加载web应用目录下类的目的了

    2019-08-08
    1
    1
  • 飞翔
    老师 您说
    java 的类加载器,就是把字节码格式.class 文件加载到JVM的方法区,并在JVM的堆区建立一个java.lang.Class 对象实例

    那loadclass方法负责把字节码格式.class 文件加载到JVM的方法区
    和defineclass负责在JVM的堆区建立一个java.lang.Class 对象实例 这样理解对嘛


    作者回复: 对的

    2019-07-23
    1
  • Nu11PointerEx
    这里有个疑问,
    原文【没有一上来就直接委托给父加载器,而是先在本地目录下加载,为了避免本地目录下的类覆盖 JRE 的核心类,先尝试用JVM 扩展类加载器 ExtClassLoader 去加载】
    如果本地目录定义了与核心库同样的类名,这样就无法保证安全了吧?

    作者回复: Tomcat的类加载器首先尝试用ExtClassLoader去加载,目的就是为了避免你说的这个问题,一旦ExtClassLoader已经加载过了,本地的同名类就不会被加载。

    2019-07-19
    1
  • 🐟🐙🐬🐆🦌🦍🐑�...
    老师 我想问下 App类加载器的类 为什么可以访问 由Ext类加载器加载的类和根类加载器加载的类。 不是不同的类加载器加载的类相互隔离么 .App类加载器和ext类加载器 和根类加载器都是不同类加载器啊。双亲委托机制是为了让类不重复加载和避免jvm类被破坏而已。那怎么说双器委派机制后 ,App类加载器可以访问Ext加载的类.不是命名空间隔离了么

    作者回复: 所谓的隔离是说同一个类加载器的不同实例加载的同名类,被认为是两个不同的类。注意说的是不同实例。

    appclassloder不会跟它的父加载器加载同名的类,也就不存在隔离的说法。

    2019-07-11
    1
  • nightmare
    我明白了,比如可以把多个项目共享的jar包放到${CATALINA_HOME}/shared目录下,让sharedclassloader来加载,并且是所有context的web应用共享的,而都有的放在web路径下,先让扩展类加载器加载,避免覆盖jre中的类,再让自定义的web加载器来加载独有的类,最后加载让应用加载器加载扩展类加载器和自定义加载器加载不到的类,谢谢李老师

    作者回复: 👍

    2019-07-04
    1
  • WL
    想问一下老师tomcat为什么采用"首先自己尝试去加载某个类,如果找不到再代理给父类加载", 这种方式呢, 我不是很理解. 还有在Tomcat的类加载器的loadClass()方法, 会先调用ExtClassLoader加载类, 然后才调用findClass(name), 这是不是与上面的"首先自己尝试去加载某个类,如果找不到再代理给父类加载"这句话矛盾呢?

    作者回复: 不矛盾的,本质还是自己先加载,这里先委托给Ext加载器纯粹为了避免类覆盖

    2019-07-04
    1
  • despacito
    是不是只要自己写的类或是引用的jar里面的类只要有类名和bootstrap 加载路径下相同的类名,都不会加载成功,但是如果不是bootstrap加载路径下的类,比如appclass loader是可以加载成功的?

    作者回复: 对的

    2019-07-04
    1
收起评论
42
返回
顶部