首页 > 其他分享 >JVM---JDBC案例是否真的打破了双亲委派机制的思考

JVM---JDBC案例是否真的打破了双亲委派机制的思考

时间:2024-11-17 09:45:36浏览次数:3  
标签:委派 DriverManager JDBC Launcher --- 双亲 JVM 机制 加载

首先先来了解一下数据库驱动的加载过程:

数据库驱动加载的过程

我们先来看看Java中SPI定义的一个核心类:DriverManager,该类位于rt.jar包中,是Java中用于管理不同数据库厂商实现的驱动,同时这些各厂商实现的Driver驱动类,都继承自Java的核心类java.sql.Driver,如MySQL的com.mysql.cj.jdbc.Driver的驱动类

DriverManager类位于rt.jar包中,由启动类加载器加载

image.png|475

我们导入的Mysql的jar包或依赖中的mysql驱动对应的类,由应用程序类加载器来加载

image.png|475

image.png|475

DriverManager属于rt.jar是启动类加载器加载的。而用户jar包中的驱动需要由应用类加载器加载,这就违反了双亲委派机制。(这点存疑,一会儿再讨论)

那么问题来了,DriverManager怎么知道jar包中要加载的驱动在哪儿?

1、在类的静态代码块中有这么一个方法LoadInitialDrivers
image.png

2、这里使用了SPI机制,去加载所有jar包中实现了Driver接口的实现类。(SPI机制不明白的可以看我的这篇文章:SPI机制

image.png|475

3、SPI机制就是在这个位置下存放了一个文件,文件名是接口名,文件里包含了实现类的类名。这样SPI机制就可以找到实现类了。如果加载数据库的话,文件名就必须是java.sql.Driver,文件内写上驱动类的全类名 com.mysql.cj.jdbc.Driver
image.png|400

image.png|400

4、SPI中利用了线程上下文类加载器(应用程序类加载器) 去加载类并创建对象。
image.png|450

总结:

image.png

线程上下文类加载器是什么时候放进去的?

在前面我们分析Java中的双亲委派实现时,曾提到了Ext、App类加载器都是Launcher类的内部类,Ext、App类加载器的初始化操作都是在Launcher构造函数中完成的,同时,在该构造函数中,Ext、App初始化完成后,会执行下面这句代码: Thread.currentThread().setContextClassLoader(loader);

// sun.misc.Launcher类
public class Launcher {
	// sun.misc.Launcher类 → 构造器
	public Launcher(){
		Launcher.ExtClassLoader var1;
		try {
			// 会先初始化Ext类加载器并创建ExtClassLoader
			var1 = Launcher.ExtClassLoader.getExtClassLoader();
		} catch (IOException var10) {
			throw new InternalError("Could not create extension class loader", var10);
		}

		try {
			// 再创建AppClassLoader并把Ext作为父加载器传递给App
			loader = AppClassLoader.getAppClassLoader(extcl);
		} catch (IOException e) {
			throw new InternalError("Could not create application class loader");
		}

		// 将APP类加载器设置为线程上下文类加载器
		Thread.currentThread().setContextClassLoader(loader);
		// 省略......
	}

通过如上这句代码,在Launcher构造函数中,会将已经创建好的AppClassLoader系统类加载器设置为默认的线程上下文类加载器。

分析到了这个地方后,可能有部会有些绕,我们稍微梳理一下总体流程:

Java程序启动 → JVM初始化C++编写的Bootstrap启动类加载器 → Bootstrap加载Java核心类(核心类中包含Launcher类) → Bootstrap加载Launcher类,其中触发Launcher构造函数 → Bootstrap执行Launcher构造函数的逻辑 → Bootstrap初始化并创建Ext、App类加载器 → Launcher类的构造函数中将Ext设置为App的父类加载器 → 同时再将App设置为默认的线程上下文类加载器Bootstrap继续加载其他Java核心类(如:SPI接口) → SPI接口中调用了第三方实现类的方法 → Bootstrap尝试去加载第三方实现类,发现不在自己的加载范围内,无法加载 → 依赖于SPI的动态服务发现机制,这些实现类会被交由线程上下文类加载器进行加载(在前面讲过,线程上下文加载器在Launcher构造函数被设置为了App类加载器) → 通过App系统类加载器加载第三方实现类,发现这些实现类在App的加载范围内,可以被加载,SPI接口的实现类加载完成…

加载流程如上,很明显的就可以感觉出来,线程上下文类加载器介入后,轻而易举的打破了原有的双亲委派模型,同时,也正是因为线程上下文类加载器的出现,从而使得Java的类加载器机制更加灵活,方便。

好了,上面分析了数据库驱动的加载过程,JDBC的案例可以说是介绍打破双亲委派机制的最好案例了。

刚才我也说到JDBC案例打破了双亲委派机制,几乎网上能搜到的文章也都说JDBC打破了双亲委派机制。但是,它整的打破了双亲委派机制吗?

问题:JDBC的案例中真的打破了双亲委派机制吗?

image.png

支持打破了双亲委派机制的理由:

先由启动类加载器去加载DriverManger,又使用了应用类加载器去加载了驱动类。但是这个过程并没有按照从上到下依次加载的过程,而是通过线程上下文类加载器获取到应用类加载器进行加载的。这是不符合双亲委派机制的加载流程的。正常的加载流程是 从下至上委派,从上至下加载。这个过程并没有从上至下加载,所以打破了双亲委派机制。

支持没有打破双亲委派机制的理由:

JDBC只是在DriverManger加载完之后,通过初始化阶段触发了驱动类的加载,类的加载依然遵循双亲委派机制

我们先来看看Java中SPI定义的一个核心类:DriverManager,该类位于rt.jar包中,是Java中用于管理不同数据库厂商实现的驱动,同时这些各厂商实现的Driver驱动类,都继承自Java的核心类java.sql.Driver,如MySQL的com.mysql.cj.jdbc.Driver的驱动类。先看看DriverManager的源码,如下:

// rt.jar包 → DriverManager类
public class DriverManager {
// .......

// 静态代码块
static {
	// 加载并初始化驱动
	loadInitialDrivers();
	println("JDBC DriverManager initialized");
}

// DriverManager类 → loadInitialDrivers()方法
private static void loadInitialDrivers() {
	// 先读取系统属性 jdbc.drivers
	String drivers;
	try {
	drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
		public String run() {
			return System.getProperty("jdbc.drivers");
		}
	});
	} catch (Exception ex) {
		drivers = null;
	}

	AccessController.doPrivileged(new PrivilegedAction<Void>() {
	public Void run() {
		//通过ServiceLoader类查找驱动类的文件位置并加载
		ServiceLoader<Driver> loadedDrivers =
		ServiceLoader.load(Driver.class);
		//省略......
	}
	});
	//省略......
}

观察如上源码,在DriverManager类的静态代码块中调用了loadInitialDrivers()方法,该方法中,会通过ServiceLoader查找服务接口的实现类。也就是说执行到了DriverManager的静态代码块时才触发了对数据库驱动类的加载,也就是在加载DriverManager初始化阶段进行的。

整体分析

  • 打破双亲委派机制的分析

    • 加载流程对比:在双亲委派机制中,类加载是遵循 “从下至上委派,从上至下加载” 的原则。对于 JDBC 中 DriverManager 和驱动类的加载,如果启动类加载器加载 DriverManager,而应用类加载器加载驱动类且不是按照正常双亲委派的流程,这确实存在与双亲委派机制冲突的地方。
    • 线程上下文类加载器的影响:当通过线程上下文类加载器获取应用类加载器来加载驱动类时,这种绕过了常规双亲委派流程的方式是关键证据。因为双亲委派机制旨在保证类加载的层级顺序,这种跨层级的加载行为可能导致同一个类在不同的类加载器中存在多个副本,破坏了双亲委派机制所期望的类加载的一致性和层级性。
  • 未打破双亲委派机制的分析

    • 加载触发角度:从这个角度看,如果认为 JDBC 只是在 DriverManager 加载完成后,通过初始化阶段触发驱动类的加载,那么在每个类加载的具体过程中可能仍然遵循双亲委派机制。即驱动类在被触发加载时,可能还是会从下至上先检查父类加载器是否已经加载过该类,再决定由哪个类加载器进行加载。
    • 整体机制的遵循:支持者可能认为虽然加载触发的方式看似特殊,但整体的类加载规则,比如类加载器之间的层级关系和加载顺序的内在逻辑在每个独立的类加载动作中没有被破坏,只是整个加载过程的触发机制比较复杂,容易被误解为打破了双亲委派机制
  • 持有打破双亲委派机制观点的更加注重类的加载过程,必须遵循 “从下至上委派,从上至下加载” 的原则;

  • 持有没有打破双亲委派机制观点的更加注重类加载器之间的层级关系和加载顺序的内在逻辑

关于JDBC是否打破双亲委派机制的问题,也是很值得讨论的~~

标签:委派,DriverManager,JDBC,Launcher,---,双亲,JVM,机制,加载
From: https://blog.csdn.net/weixin_73205368/article/details/143824414

相关文章

  • Java---SPI机制
    何谓SPI?SPI即ServiceProviderInterface,字面意思就是:“服务提供者的接口”,我的理解是:专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用......
  • 南京邮电大学-鲁健
    一、个人简介        2021年9月就读于南京邮电大学自动化学院、人工智能学院智能科学与技术专业,主修人工智能方向的相关课程,极其擅长具身智能的相关开发,曾被誉为“原批之星”,最有希望颠覆三国杀的人之一。        志愿活动:参加2023年度自动化学院、人工智能......
  • 基于Java+Vue+MySQL的青少年信息学奥林匹克竞赛交流平台设计与实现(精选毕业设计-可设
    文章目录1.前言2.详细视频演示3.文档参考3.1论文参考3.2流程设计图3.3数据库表结构设计3.4系统测试部分4.项目运行截图5.技术框架5.1后端采用SpringBoot框架5.2前端框架Vue6.选题推荐毕设案例8.系统测试8.1系统测试的目的8.2系统功能测试9.代码参考10......
  • Java毕业设计-基于Springboot框架的电影推荐系统项目实战(附源码+论文)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 基于SSM + Vue的宠物店线上运营系统设计与实现(精选毕业设计-可设计亮点、创新点)
    文章目录1.前言2.详细视频演示3.文档参考3.1论文参考3.2流程设计图3.3数据库表结构设计3.4系统测试部分4.项目运行截图5.技术框架5.1后端采用SpringBoot框架5.2前端框架Vue6.选题推荐毕设案例8.系统测试8.1系统测试的目的8.2系统功能测试9.代码参考10......
  • Java毕业设计-基于Springboot框架的兴顺物流管理系统项目实战(附源码+论文)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 2024第四届京津冀长城杯-misc
    2024第四届京津冀长城杯-miscBrickGame就连连看或者改图标会快一点吧漏洞探踪,流量解密第一阶段192.168.30.234第二阶段bdb8e21eace81d5fd21ca445ccb35071bdb8e21eace81d5fd21ca445ccb350715a76f6751576dbe1af49328aa1d2d2bea16ef62afa3a7c616dbdb8e21eace81d5fd21ca445ccb35071......
  • wish easy-记一道re题
    wisheasy-记一道re题F5设置环境变量exportPYTHONINSPECT=111exportPYTHONUNBUFFERED=11运行一下flag{python_taken_2_far}......
  • DIDCTF-2022暑假取证学习
    DIDCTF-2022暑假取证学习1.请找出操作系统主机名WIN-49I0SNRJAMF2.请给出源磁盘的SHA256哈希值。这个软件没找到Autopsy这个软件计算时间太长了,就...3.请找出操作系统中安装的Android模拟器名称和安装日期。格式:模拟器名时间例子:雷电模拟器2022年06月23日夜神模拟器2021年05年03......
  • NSSRound#12 Basic-ordinary forensics
    NSSRound#12Basic-ordinaryforensics[NSSRound#12Basic]ordinaryforensicsvol.py-fforensics.raw--profile=Win7SP1x64cmdscan找到了一个密码U_find_1tvol.py-fforensics.raw--profile=Win7SP1x64filescan|grepzip·提取这个压缩包vol.py-fforensics.raw--profi......