采用双亲委托机制的原因

  类加载器就是将字节码搬进jvm方法区的组件。我们知道,JVM识别加载进来的类是通过类加载器+类全名完成的,也就是说同一个类由不同类加载器加载进去的话就会被视为不同的类。jdk提供的类加载器及他们的关系如下:

我们可以自己继承ClassLoader定义多个自己的类加载器,但是这多个类加载器去加载同一个类(例如java.lang.Integer)就会多次加载这个类,jvm也会将这两次加载的Integer类看成不同的类,因为加载器不是同一个。而我们希望的是,对于这种核心基础类,只需加载一个就行了。此外,我们甚至可以自定义一个Integer类,用自定义的加载器加载进去,这对于程序来说是不安全的。

基于以上两个原因,Java采用了双亲委托机制。工作流程:

  1. 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。每个类加载器都有加载缓存,当一个类被加载了以后就会放入缓存,下次加载的时候就可以直接返回。

  2.  当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父的父去加载,一直到BootStrapClassLoader.

  3.  到BootStrapClassLoader都没有发现缓存的话,就开始主动加载。(加载成功将其放入它自己的缓存中,以便下次有加载请求的时候直接返回)

  4. 如果BootStrapClassLoader在自己负责加载的文件夹中找不到目标类,则将任务交给下一层主动加载,不成功继续交给下一层主动加载。

总结:先层层往上找有没有缓存,到顶后还没缓存就主动加载,失败就层层往下尝试。

因此使用此机制原因归为:

  1. 使类加载器有优先关系,防止了重复加载。父类加载了子类便不会去加载。例如:核心的基础类只会被引导类加载器加载,保证了核心基础类在jvm的唯一性即一个类总是由一个类加载器加载

  2. 更安全。冒充核心类名的类最终总是到引导类记载器。保证了核心基础类的正确性即只会加载指定包中唯一的类

破坏双亲委托机制的情形

  双亲委托机制并不是强制使用的,我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name)方法。

  下面例举破坏双亲模型的情形。

1. Tomcat

  Tomcat是一个servlet容器,他也是Java编写的,需要运行在JVM之上。我们编写的服务端程序即servlet部署在tomcat中才是完整的服务端程序(tomcat完成了与客户端通信、管理servlet生命周期等功能)。一般生产环境下,一个Tomcat中会运行着多个servlet,这个时候我们就不应该使用双亲模型了。原因是多个servlet应该是完全隔离的,而使用双亲模型,多个服务器程序就是共用一套类库了,这样各种错误都来了。

  正确的做法是,每个部署在tomcat中的servlet都有自己的WebAppClassLoader,加载类时直接使用它加载,这样各个servlet加载的类互不干涉。

  一个Tomcat启动会伴随一个JVM进程的启动(因为Tomcat也是Java编写的),多个部署在Tomcat的应用可以互不干涉的运行在这一个JVM中,正式因为破坏了双亲委托机制。

2. JDBC

  使用JDBC建立数据库连接:

Connection con = DriverManager.getConnection(url , username , password ) ; 

JDBC的Driver接口定义在JDK中,但是其具体实现由各个数据库的服务商来提供,比如MySQL驱动包。

DriverManager 类位于 $JAVA_HOME中jre/lib/rt.jar 包,由BootStrap类加载器加载,而其具体Driver实现类是位于服务商提供的 Jar 包。根据类加载机制,当被装载的类引用了另外一个类的加载时候,虚拟机就会使用装载第一个类的类装载器装载被引用的类,也就是说BootStrap类加载器还要去加载jar包中的Driver接口的实现类,但BootStrap类加载器默认只负责加载 $JAVA_HOME中jre/lib里的class,所以又需要由子类加载器去加载Driver实现,这就破坏了双亲委派模型。

实际的Tomcat类加载机制

  既然Tomcat破坏了双亲模型,那么是否表示他可能会装载到自定义的有害的基础类呢?先看看Tomcat类加载器的结构(图片来源于网络https://www.cnblogs.com/aspirant/p/8991830.html):

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;

每个应用在部署后,都会创建一个唯一的类加载器,即自己的WebAppClassLoader,该类加载器会加载的类仅仅对本应用可见。Web应用程序的WebApp类加载器加载类要加载某类时,该类加载器将首先查找自己负责的文件夹中是否存在此类,而不是向父加载器委派。也有例外,属于JRE基类的类不能如此,还是要走双亲委派。

  综上,Tomcat也有一套层次结构的类加载器,加载JRE基类时还是走双亲委派,其他就是优先使用WebApp加载器加载。因此,即使破坏了双亲模型,Tomcat类加载也不会有安全问题。

破坏双亲委托机制的一些情况---Tomcat和JDBC,破坏后的安全问题的更多相关文章

  1. JAVA基础|从Class.forName初始化数据库到SPI破坏双亲委托机制

    代码托管在:https://github.com/fabe2ry/classloaderDemo 初始化数据库 如果你写过操作数据库的程序的话,可能会注意,有的代码会在程序的开头,有Class.for ...

  2. ClassLoader类加载器 & Java类加载机制 & 破坏双亲委托机制

    ClassLoader类加载器 Java 中的类加载器大致可以分成两类: 一类是系统提供的: 引导类加载器(Bootstrap classloader):它用来加载 Java 的核心库(如rt.jar ...

  3. Java 类加载体系之 ClassLoader 双亲委托机制

    Java 类加载体系之 ClassLoader 双亲委托机制 java 是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件 ...

  4. jdbc 加载数据库驱动如何破坏双亲委托模式

    导读      通过jdbc链接数据库,是每个学习Java web 方向的人必然一开始会写的代码,虽然现在各路框架都帮大家封装好了jdbc,但是研究一下jdbc链接的套路还是很意义     术语以及相 ...

  5. JVM 类加载器的双亲委托机制

    1.类加载器的层次结构 在双亲委托机制中,各个加载器按照父子关系形成了树形结构(逻辑意义),除了根加载器之外,其余的类加载器都有且只有一个父加载器. public class MyTest13 { p ...

  6. 【JVM学习笔记】双亲委托机制存在的意义

    1.可以确保Java核心库的类型安全:所有的Java应用都至少会引用java.lang.Object类,也就是说在运行期,java.lang.Object这个类会被加载到Java虚拟机:如果用户自定义 ...

  7. 【JVM学习笔记】打破双亲委托机制的例子

    Tomcat也有自己的类加载器,比如Servlet,这些类加载器就改变了双亲委托模型的默认机制 (该主题有待深入)

  8. 说一说JVM双亲委派机制与Tomcat

    双亲委派模型与JVM 类加载 讲个故事: 以前,爱捣鼓的小明突然灵机一动,写出了下面的代码 package java.lang; public class String { //...复制真正Stri ...

  9. [转帖]说一说JVM双亲委派机制与Tomcat

    说一说JVM双亲委派机制与Tomcat https://www.cnblogs.com/dengchengchao/p/11844022.html 讲个故事: 以前,爱捣鼓的小明突然灵机一动,写出了下 ...

随机推荐

  1. 【zabbix】zabbix 高可用架构的实现

    https://www.jianshu.com/p/249d47b089b4?utm_campaign=maleskine&utm_content=note&utm_medium=se ...

  2. python开发环境准备

    python 以版本众多,包之间依赖复杂而著称,所以一个趁手的开发环境还是很有必要的. 我的建议是用Anaconda做环境隔离.包管理,PyCharm做项目开发,jupyter做笔记,ipython和 ...

  3. Linux驱动开发3——devfs udev procfs sysfs debugfs傻傻地分不清楚

    Linux调试文件系统 1.1.procfs 早期的Linux内核中,内核通过procfs输出调试信息,可以在用户态通过读写procfs节点与内核进行交互,用来获取处理器.内存.设备驱动.进程等各种信 ...

  4. 线性代数之——SVD 分解

    SVD 分解是线性代数的一大亮点. 1. SVD 分解 \(A\) 是任意的 \(m×n\) 矩阵,它的秩为 \(r\),我们要对其进行对角化,但不是通过 \(S^{-1}A S\).\(S\) 中的 ...

  5. 07 oracle 非归档模式 inactive/active/current redo log损坏的恢复

    在非归档模式下缺失Redo Log后的恢复 将之前的归档模式修改为非归档 SQL> shutdown immediate; SQL> startup mount SQL> alter ...

  6. struts2 基础4 验证器、 国际化

    验证器: 验证器:用户输入验证 1.手动编程方式 )对于动作类中所有方法进行验证 a.动作类继承ActionSuport b.覆盖调用public void validate(){} 方法 c.在va ...

  7. review-1

    # ### for 循环和序列的运用 # remember = ['从入门到放弃', '从入门到如土', 123, 'happy']# for each in remember:# print(eac ...

  8. pandas 入门(2)

    from pandas import Series, DataFrame, Index import numpy as np from numpy import nan as NA obj = Ser ...

  9. python控制流-提前结束进程

    一.sys.exit() 调用 sys.exit()函数,可以让程序终止或退出. 这个函 数在 sys 模块中,必须先导入 sys,才能使用它: #!/usr/bin/env python #codi ...

  10. Vue—非父子组件间的传值(Bus/发布订阅模式/观察者模式/总线)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...