Tomcat的类加载机制和JVM的类加载机制

一、什么是类加载?

java文件需要被编译成.class文件,才能被JVM加载到内存中,这个过程就是类加载的过程。

二、什么是类加载器

  1. 类加载器也是一个类,JVM启动时先加载类加载器,类加载器再去加载其他的类(比如各种jar的字节码文件、项目编译后的class字节码文件)
  2. 对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。当我们比较两个类是否相等的时候,前提是:只有在这两个类是由同一个类加载器加载为前提下才有意义的。
  3. 启动类加载器是JVM的一部分,由虚拟机是别的类库加载到虚拟机内存中。不在java类库中,在程序中只能获取到其引用,并且不允许通过引用对其进行操作。
  4. 拓展类加载器在java9中被删除了,替换为了Platform加载器,原因是拓展类加载器直接加载ext目录下的jar的做法不安全,同时java9提倡模块化开发的拓展能力取代了这种拓展机制。
  5. SystemClassLoader(系统类加载器)不是一个全新的加载器,它只是一个概念引用,系统类加载器可以通过System.setProperty(“java.system.class.loader”, xxx类名)进行自定义设置
//可以通过这种方式打印加载路径
System.out.println("boot:"+System.getProperty("sun.boot.class.path"));
System.out.println("ext:"+System.getProperty("java.ext.dirs"));
System.out.println("app:"+System.getProperty("java.class.path"));



三、JVM中的类加载器结构



四、JVM的双亲委派机制

4.1 什么是双亲委派机制

概念: 当某个类加载器,需要加载某个.class文件时,首先将这个任务委托给其上级类加载器加载,递归这个操作,如果没有上级加载器或者上级加载器没有加载,就由其本身去加载这个类。
注意:

  1. ClassLoader类是一个抽象类,但却没有包含任何抽象方法。
  2. 如果要实现自己的类加载器且不破坏双亲委派模型,只需要继承ClassLoader类并重写findClass方法。
  3. 如果要实现自己的类加载器且破坏双亲委派模型,则需要继承ClassLoader类并重写loadClass,findClass方法。

4.2 双亲委派机制解决了什么问题

  1. 防止重复加载同一个.class ,保证数据安全
  2. 保证核心的class不会被篡改覆盖。不同的加载器加载的同一个.class文件,也不是同一个class对象,保证class的执行安全。

4.3 为什么说JDBC加载驱动时违背了双亲委派机制

参考博客:https://blog.csdn.net/atongmu2017/article/details/93649837

4.3.1 SPI机制和双亲委派的冲突

  • SPI全名是Service Provide Interface ,当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件,该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。JDBC SPI mysql的实现如下所示。

image.png

  • JAVA为提供了很多服务SPI,这些SPI的接口由Java核心库来定义,例如JDBC,而具体的实现则是具体的厂商自己实现。只要classpath中引入具体的实现的jar即可。但是SPI的接口在核心库中,是由启动类加载器加载的,但是启动类加载器无法找到classpath中的SPI实现类,这就是双亲委派机制带来的问题。
  • 线程上下文类加载器整好解决了这个问题。但是它违背了JAVA推荐的双亲委派机制,让程序可以逆向使用类加载器。使得java的类加载体系更灵活。

4.3.2 线程上下文加载器

  1. contextClassLoader是java1.2时引入的。Java.lang.Thread的getContextClassLoader() setContextClassLoader(ClassLoader cl) 可以设置或者获取上下文加载器。
  2. sun.misc.Launcher 初始化拓展类加载器,和应用类加载器时会设置上下文加载器为应用类加载器。

4.3.3 JDBC加载驱动时怎么违背了双亲委派机制

//JDBC规范中明确要求Driver(数据库驱动)类必须向DriverManager注册自己
//JDBC注册驱动的几种方式

//方式1 Class.forName(“com.mysql.jdbc.Driver”)
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, password);

//方式2 System.setProperty(“jdbc.drivers”,“com.mysql.jdbc.Driver”)
System.setProperty("jdbc.drivers", driver);
conn = (Connection)DriverManager.getConnection(url, user, passwd);

//方式3 SPI 服务提供机制自动注册
conn = (Connection)DriverManager.getConnection(url, user, passwd);

  • 方式1 Class.forName(“com.mysql.jdbc.Driver”) 通过当前调用者的类加载器来加载驱动类,并在初始化驱动类时,向DriverManager注册自己
  • 方式2 通过系统的属性设置注册驱动,最终还是通过系统类加载器完成。
  • 方式3 SPI机制自动注册
    • JDK1.6开始,Oracle就修改了加载JDBC驱动的方式,即JDBC4.0。在JDBC 4.0中,我们不必再显式使用Class.forName()方法明确加载JDBC驱动。
    • 通过SPI方式,读取 META-INF/services 下文件中的类名,使用线程上下文类加载器加载;

五、Tomcat的类加载机制

最大的改变:为了保证不同的Web应用,可以使用同名不同版本的jar而互不干扰,所以没有完全遵守双亲委派加载机制。

5.1 Tomcat的类加载器构成

  1. 引导类加载器和拓展类加载器的作用不变
  2. 系统类加载器,正常的是加载classpath下的类,但是Tomcat中,系统类加载器是去加载Tomcat启用的类库,比如bootstrap.jar ,通常可以在catalina.bat/sh中可以指定
  3. 公共类加载器,加载Tomcat和所有Web应用通用的类 CATALINA_HOME/lib下,⽐如servlet-api.jar
  4. Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问
  5. Shared ClassLoader ⽤于加载应⽤程序共享类,这些类Tomcat服务器不会依赖.
  6. Webapp ClassLoader,每个应⽤程序都会有⼀个独⼀⽆⼆的Webapp ClassLoader,他⽤来加载本应⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。

5.2 Tomcat类加载器加载类的过程

  1. 先在本地 cache 缓存中查找该类是否已经加载过,看看 Tomcat 有没有加载过这个类
  2. 如果 Tomcat 没有加载过这个类,则从系统类加载器的 cache 缓存中查找是否加载过
  3. 如果没有,则使用 ExtClassLoader 类加载器类加载,重点来了,Tomcat 的 WebAppClassLoader 并没有先使用 AppClassLoader 来加载类,而是直接使用了 ExtClassLoader 来加载类。不过 ExtClassLoader 依然遵循双亲委派,它会使用 Bootstrap ClassLoader 来对类进行加载,保证了 Jre 里面的核心类不会被重复加载。比如在 Web 中加载一个 Object 类。WebAppClassLoader → ExtClassLoader → BootstrapClassLoader,这个加载链,就保证了 Object 不会被重复加载。
  4. 如果没有加载成功,WebAppClassLoader 就会调用自己的 findClass() 方法由自己来对类进行加载,先在 WEB-INF/classes 中加载,再从 WEB-INF/lib 中加载。
  5. 如果仍然未加载成功,WebAppclassLoader 会委派给 SharedClassLoader,SharedClassLoad 再委派给 CommonClassLoader,CommonClassLoader 委派给 AppClassLoader,直到最终委派给 BootstrapClassLoader,最后再一层一层地在自己目录下对类进行加载。
  6. 都没有加载成功的话,抛出异常。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值