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把创建类加载器的细节进行封装,可以通过它方便的创建自定义类加载器。

使用加载器工厂的好处

  1. ClassLoadFactory有一个内部Repository,它就是表示资源的类,资源的类型用一个RepositoryType的枚举表示。
  2. 同时我们看到,如果在检查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源码分析(类加载与类加载器)的更多相关文章

  1. Tomcat源码分析之—具体启动流程分析

    从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...

  2. Tomcat源码分析——启动与停止服务

    前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家 ...

  3. Tomcat 源码分析(转)

    本文转自:http://blog.csdn.net/haitao111313/article/category/1179996 Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件 ...

  4. Tomcat源码分析 (四)----- Pipeline和Valve

    在 Tomcat源码分析 (二)----- Tomcat整体架构及组件 中我们简单分析了一下Pipeline和Valve,并给出了整体的结构图.而这一节,我们将详细分析Tomcat里面的源码. Val ...

  5. Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析

    Tomcat启动加载过程(一)的源码解析 今天,我将分享用源码的方式讲解Tomcat启动的加载过程,关于Tomcat的架构请参阅<Tomcat源码分析二:先看看Tomcat的整体架构>一文 ...

  6. tomcat 源码分析

    Tomcat源码分析——Session管理分析(下)    Tomcat源码分析——Session管理分析(上)     Tomcat源码分析——请求原理分析(下)     Tomcat源码分析——请 ...

  7. Tomcat源码分析——Session管理分析(下)

    前言 在<TOMCAT源码分析——SESSION管理分析(上)>一文中我介绍了Session.Session管理器,还以StandardManager为例介绍了Session管理器的初始化 ...

  8. Tomcat源码分析——Session管理分析(上)

    前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...

  9. Tomcat源码分析一:编译Tomcat源码

    Tomcat源码分析一:编译Tomcat源码 1 内容介绍 在之前的<Servlet与Tomcat运行示例>一文中,给大家带来如何在Tomcat中部署Servlet应用的相关步骤,本文将就 ...

  10. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

随机推荐

  1. C/C++陷阱与套路,当年就是折在这些地儿…

    摘要:本文结合作者的工作经验和学习心得,对C++语言的一些高级特性,做了简单介绍:对一些常见的误解,做了解释澄清:对比较容易犯错的地方,做了归纳总结:希望借此能增进大家对C++语言了解,减少编程出错, ...

  2. 修改mac系统名字&&神秘bogon

    问题分析 你是否遇见过突然终端突然出现奇怪 bogon # name @ bogon in ~ [22:31:01] $ 这是因为终端会先向 DNS 请求查询当前 IP 的反向域名解析的结果,如果查询 ...

  3. java基础之字符串

    以下内容摘自<java编程思想>第十三章. 1. 不可变 String String 对象是不可变对象,String 类中每一个看起来会修改 String 值的方法,实际上都是创建了一个全 ...

  4. SpringBoot2 整合Ehcache组件,轻量级缓存管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.Ehcache缓存简介 1.基础简介 EhCache是一个纯Java的进程内缓存框架,具有快速.上手简单等特点,是Hibernate中默认 ...

  5. C++ 的字符串反转

    C++ 的字符串反转 方法一: 使用 algorithm 中的 reverse 函数: // reverse 函数的定义(在 std 名称空间中) template<class BidirIt& ...

  6. C#LeetCode刷题之#876-链表的中间结点(Middle of the Linked List)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3836 访问. 给定一个带有头结点 head 的非空单链表,返回链 ...

  7. 漏洞重温之XSS(中)

    漏洞重温之XSS(中) XSS挑战之旅 level8-level13 level8 第八关开局,发现button从搜索变成了友情链接,发现该页面情况跟前面不同,先右键查看代码,再进行尝试. 上测试代码 ...

  8. 关于Dapper实现读写分离的个人思考

    概念相关     为了确保多线上环境数据库的稳定性和可用性,大部分情况下都使用了双机热备的技术.一般是一个主库+一个从库或者多个从库的结构,从库的数据来自于主库的同步.在此基础上我们可以通过数据库反向 ...

  9. Android 重写物理返回键,在h5页面中返回上一个界面

    实现:Activity中放置webview,跳转到h5界面,点击返回键,不退出h5界面,而是返回上一个h5界面 /** * 改写物理按键--返回的逻辑,希望浏览的网页后退而不是退出浏览器 * @par ...

  10. 开发过程中遇到的-npm加载进node_modules里的文件怎么修改

    来源:https://juejin.im/post/5ec381215188256d776342cd https://mp.weixin.qq.com/s?__biz=MzUzNjk5MTE1OQ== ...