Tomcat源码分析(类加载与类加载器)
Tomcat的挑战
Tomcat上可以部署多个项目
Tomcat的一般部署,可以通过多种方式启动一个Tomcat部署多个项目,那么Tomcat在设计时会遇到什么挑战呢?
Tomcat运行时需要加载哪些类
Tomcat中的多个项目可能存在相同的类
Tomcat中类加载的挑战
源码分析彻底弄懂Tomcat的类加载
类加载与类加载器
类加载
类加载:主要是将.class文件中的二进制字节读入到JVM中
我们可以看到因为这个定义,所以并没有规定一定是要磁盘加载文件,可以通过网络,内存什么的加载。只要是二进制流字节数据,JVM就认。
类加载过程:
1.通过类的全限定名获取该类的二进制字节流;
2.将字节流所代表的静态结构转化为方法区的运行时数据结构
3.在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
类加载器
定义:JVM设计者把“类加载”这个动作放到java虚拟机外部去实现,以便让应用程序决定如何获取所需要的类。实现这个动作的代码模块成为“类加载器”
类与类加载器
对于任何一个类,都需要由加载它的类加载器和这个类来确定其在JVM中的唯一性。也就是说,两个类来源于同一个Class文件,并且被同一个类加载器加载,这两个类才相等。
注意:这里所谓的“相等”,一般使用instanceof关键字做判断。
类加载器与双亲委派模型
类加载器
启动类加载器:该加载器使用C++语言实现,属于虚拟机自身的一部分。
启动类加载器(Bootstrap ClassLoader):负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库加载到JVM内存中,如果名称不符合的类库即使在lib目录中也不会被加载。该类加载器无法被java程序直接引用
拓展类加载器与应用程序类加载器:另一部分就是所有其它的类加载器,这些类加载器是由Java语言实现,独立于JVM外部,并且全部继承抽象类java.lang.ClassLoader。
扩展类加载器(Extension ClassLoader):该加载器主要负责加载JAVA_HOME\lib\ext目录中的类库,开发者可以使用扩展加载器。
应用程序类加载器(Application ClassLoader):该列加载器也称为系统加载器,它负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器
双亲委派模型
定义:双亲委派模型的工作过程为:如果一个类加载器收到了类请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父加载器去完成,每一层都是如此,因此所有类加载的请求都会传到启动类加载器,只有当父加载器无法完成该请求时,子加载器才去自己加载。
实现方式:该模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。子类加载器不是以继承的关系来实现,而是通过组合关系来复用父加载器的代码。
意义:好处双亲委派模型的好处就是java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如:Object,无论那个类加载器去加载该类,最终都是由启动类加载器进行加载的,因此Object类在程序的各种类加载环境中都是一个类。如果不用改模型,那么java.lang.Object类存放在classpath中,那么系统中就会出现多个Object类,程序变得很混乱。
双亲委派模型
从虚拟机的角度来说,有两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),该加载器使用C++语言实现,属于虚拟机自身的一部分。另一部分就是所有其它的类加载器,这些类加载器是由Java语言实现,独立于JVM外部,并且全部继承抽象类java.lang.ClassLoader.
从java开发人员的角度看,大部分java程序会用到以下三种系统提供的类加载器:
1、启动类加载器(Bootstrap ClassLoader):负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库加载到JVM内存中,如果名称不符合的类库即使在lib目录中也不会被加载。该类加载器无法被java程序直接引用。
2、扩展类加载器(Extension ClassLoader):该加载器主要负责加载JAVA_HOME\lib\ext目录中的类库,开发者可以使用扩展加载器。
3、应用程序类加载器(Application ClassLoader):该列加载器也称为系统加载器,它负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
JVM中的类加载器源码分析
AppClassLoader

URLClassLoader

SecureClassLoader

Tomcat中的类加载解决方案
Tomcat类加载的考虑
隔离性
Web应用类库相互隔离,避免依赖库或者应用包相互影响,比如有两个Web应用,一个采用了Spring 4,一个采用了Spring 5,而如果如果采用同一个类加载器,那么Web应用将会由于jar包覆盖而无法启动成功。


灵活性
因为隔离性,所以Web应用之间的类加载器相互独立,如果一个Web应用重新部署时,该应用的类加载器重新加载,同时不会影响其他web应用。
比如:不需要重启Tomcat的创建xml文件的类加载,
还有context元素中的reloadable字段:如果设置为true的话,Tomcat将检测该项目是否变更,当检测到变更时,自动重新加载Web应用。
性能
由于每一个Web应用都有一个类加载器,所以在Web应用在加载类时,不会搜索其他Web应用包含的Jar包,性能自然高于只有一个类加载的情况。
Tomcat中的类加载器
Tomcat提供3个基础类加载器(common、catalina、shared)和Web应用类加载器。

Tomcat中的类加载源码分析
三个基础类加载器
介绍
3个基础类加载器的加载路径在catalina.properties配置,默认情况下,3个基础类加载器的实例都是一个。
createClassLoader调用ClassLoaderFactory属于一种工厂模式,并且都是使用URLClassLoader



默认情况三个是一个实例,但是可以通过修改配置创建3个不同的类加载机制,使它们各司其职。
举个例子:如果我们不想实现自己的会话存储方案,并且这个方案依赖了一些第三方包,我们不希望这些包对Web应用可见,因此我们可以配置server.loader,创建独立的Catalina类加载器。
共享性:
Tomcat通过Common类加载器实现了Jar包在应用服务器与Web应用之间的共享,
通过Shared类加载器实现了Jar包在Web应用之间的共享
通过Catalina类加载器加载服务器依赖的类。

类加载工厂
因为类加载需要做很多事情,比如读取字节数组、验证、解析、初始化等。而Java提供的URLClassLoader类能够方便的将Jar、Class或者网络资源加载到内存中。而Tomcat中则用一个工厂类,ClassLoaderFactory把创建类加载器的细节进行封装,可以通过它方便的创建自定义类加载器。






使用加载器工厂的好处
- ClassLoadFactory有一个内部Repository,它就是表示资源的类,资源的类型用一个RepositoryType的枚举表示。
- 同时我们看到,如果在检查jar包的时候,如果有检查的URL地址的如果检查有异常就忽略掉,可以确保部分类加载正确。
尽早设置线程上下文类加载器

每一个运行线程中有一个成员ContextClassLoader,用于在运行时动态载入其他类,当程序中没有显示声明由哪个类加载器去加载哪个类(比如new出一个类时),将默认由当前线程类加载器加载,所以一般系统默认的ContextClassLoad是系统类加载器。
一般在实际的系统上,使用线程上下文类加载器,可以设置不同的加载方式,这个也是Java灵活的类加载方式的体现,也可以很轻松的打破双亲委派模式,同时也会避免类加载的异常。
Webapp类加载器
每个web应用会对一个Context节点,在JVM中就会对应一个org.apache.catalina.core.StandardContext对象,而每一个StandardContext对象内部都一个加载器实例loader实例变量。这个loader实际上是WebappLoader对象。而每一个 WebappLoader 对象内部关联了一个 classLoader 变量(就这这个类的定义中,可以看到该变量的类型是org.apache.catalina.loader.WebappClassLoader)。
所以,这里一个web应用会对应一个StandardContext 一个WebappLoader 一个WebappClassLoader 。
一个web应用对应着一个StandardContext实例,每个web应用都拥有独立web应用类加载器(WebappClassLoader),这个类加载器在StandardContext.startInternal()中被构造了出来。


注意这里:设置加载器和获取加载器都使用了读写锁机制,确保多线程情况下对共享资源的访问不会出现问题。
同时因为Tomcat的生命周期管理,必定会调用WebappLoader.java的startInternal()方法,该方法中new出了


所以总结一句话,如果没有弄懂Tomcat的启动流程,以及弄懂Tomcat的生命周期的管理,很多地方的源码是没有办法没有看懂,所以看源码也有一个先后顺序。
热加载源码分析
当配置信息中有reloadable的属性,并且值为true时,Tomcat怎么去完成这个过程呢?

还是看源码,据Tomcat的启动流程,我们分析下Context的初始化start方法,根据之前的课程我们可知道,Context只是一个接口,具体实现类是StandardContext,我们分析下startInternal方法(此方法由之前的抽闲骨架类中的start方法触发)

我发现有一个线程启动的方法 threadStart(),

由上图我们知道,这个是父类中调用,




Tomcat源码分析(类加载与类加载器)的更多相关文章
- Tomcat源码分析之—具体启动流程分析
从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...
- Tomcat源码分析——启动与停止服务
前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家 ...
- Tomcat 源码分析(转)
本文转自:http://blog.csdn.net/haitao111313/article/category/1179996 Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件 ...
- Tomcat源码分析 (四)----- Pipeline和Valve
在 Tomcat源码分析 (二)----- Tomcat整体架构及组件 中我们简单分析了一下Pipeline和Valve,并给出了整体的结构图.而这一节,我们将详细分析Tomcat里面的源码. Val ...
- Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析
Tomcat启动加载过程(一)的源码解析 今天,我将分享用源码的方式讲解Tomcat启动的加载过程,关于Tomcat的架构请参阅<Tomcat源码分析二:先看看Tomcat的整体架构>一文 ...
- tomcat 源码分析
Tomcat源码分析——Session管理分析(下) Tomcat源码分析——Session管理分析(上) Tomcat源码分析——请求原理分析(下) Tomcat源码分析——请 ...
- Tomcat源码分析——Session管理分析(下)
前言 在<TOMCAT源码分析——SESSION管理分析(上)>一文中我介绍了Session.Session管理器,还以StandardManager为例介绍了Session管理器的初始化 ...
- Tomcat源码分析——Session管理分析(上)
前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...
- Tomcat源码分析一:编译Tomcat源码
Tomcat源码分析一:编译Tomcat源码 1 内容介绍 在之前的<Servlet与Tomcat运行示例>一文中,给大家带来如何在Tomcat中部署Servlet应用的相关步骤,本文将就 ...
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
随机推荐
- Linux文本处理详细教程
1. 文本处理 本节将介绍Linux下使用Shell处理文本时最常用的工具: find.grep.xargs.sort.uniq.tr.cut.paste.wc.sed.awk: 提供的例子和参数都是 ...
- Visual Studio Code中设置sftp同步代码到服务器
## **前言** - 绝对的大佬才会直接在Linux下用vim写代码,我等小白只能通过IDE来了,所以将代码同步到服务器上就很重要了.使用vs code设置好sftp就可以实现这一功能. - 设置之 ...
- C#LeetCode刷题之#217-存在重复元素(Contains Duplicate)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3772 访问. 给定一个整数数组,判断是否存在重复元素. 如果任何 ...
- Vue element-ui el-table阻止行选事件
我们经常会在某个table末尾加上操作列来放置button来处理跳转和其他的逻辑 那么当点击button的时候同样也会执行在el-table 设置的 @row-click="handleRo ...
- 怎么写简历,简历才不会被丢到非洲🌍
前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y 最近的三歪朋友圈可以看到很多的字节.腾讯的同学都 ...
- 记录使用Python登录浙江大学统一身份认证
背景 现在每天要进行健康情况上报,但是因为经常睡过头忘记打卡,于是想着写一个程序来自动打卡. 统一身份认证 访问健康情况上报页面(https://healthreport.zju.edu.cn/nco ...
- Fiddler+模拟器+APP抓包HTTPS 为什么有时候抓不到?
抓包的原理是什么? 代理 客户端请求 -> 经过代理 -> 到达服务端 服务端返回 -> 经过代理 -> 到达客户端 任何Https的App都能抓到包么? Android7.0 ...
- importTSV工具导入数据到hbase
1.建立目标表test,确定好列族信息. create'test','info','address' 2.建立文件编写要导入的数据并上传到hdfs上 touch a.csv vi a.csv 数据内容 ...
- MapReduce框架原理
MapReduce框架原理 3.1 InputFormat数据输入 3.1.1 切片与MapTask并行度决定机制 1.问题引出 MapTask的并行度决定Map阶段的任务处理并发度,进而影响到整个J ...
- 如何选择一台适合Java开发的电脑
前言 最近在群里有同学求推荐Java开发用的电脑,所以胖哥就出个简单的专题,用我贫瘠的电脑知识来帮助大家选择适合开发的电脑配置.因为家里的主机已经带不动两个 IDEA 了,更别提开个 Docker 啥 ...