spi机制打破双亲委派机制

本文涉及的产品
性能测试 PTS,5000VUM额度
函数计算FC,每月15万CU 3个月
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 在JDBC4及以上版本,连接MySQL数据库不再需要显式加载驱动(`Class.forName`),而是利用SPI机制。系统通过扫描`META-INF/services/java.sql.Driver`文件找到`com.mysql.cj.jdbc.Driver`并使用`ServiceLoader`由AppClassLoader加载。`DriverManager`在启动时加载所有可用的`Driver`实现,实现解耦和动态发现。虽然看起来逆向了双亲委派,但实际上每个类仍由适当的类加载器加载,保持了加载层次。

JDBC连接数据库操作代码

        // 加载数据库驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 建立连接
        conn = DriverManager.getConnection(url, user, password);

核心流程比较简单,无非是加载数据库驱动、获取数据库连接、创建Statement对象,之后执行相应的数据库操作

在JDBC4以后不用显示的使用 Class.forName加载mysql的驱动,而是系统自动发现识别并注册数据库驱动。

这是因为,在JDBC4以采用了SPI机制


SPI(Service Provider Interface)是Java平台内置的一种服务提供发现机制,它允许应用程序动态地加载和使用第三方提供的服务实现,而无需在代码中显式引用这些实现类。

SPI机制的核心思想是解耦,即将接口和其具体实现分离,这种机制在模块化设计中非常重要,因为它提高了框架的扩展性和可维护性。在Java中,SPI机制通常在Classpath路径下的META_INF/services文件夹中实现,其中以接口的全限定名命名文件,文件内容为接口的实现类的全限定名。这些实现类可以通过ServiceLoader类加载并实例化,ServiceLoader使用迭代器模式来加载实现类。

在我们引入的数据库连接mysql-connector-java.jar包中META_INF/services目录下,存在一个以java.sql.Driver命名的文件,java.sql.Driver为Driver接口的全限定类名,文件中定义的内容com.mysql.cj.jdbc.Driver为对应MySQL数据库驱动实现类的全限定类名:

image-20240707163325410

这里通过调用Thread.currentThread().getContextClassLoader()方法,获取线程上下文的类加载器,使用线程上下文类加载器完成对MySQL驱动的加载
public class DriverManager {
   

    /**
     * 加载初始JDBC驱动程序
     */
    static {
   
        loadInitialDrivers();
    }

    private static void loadInitialDrivers() {
   
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
   
            public Void run() {
   

                // 使用ServiceLoader类加载并实例化Driver
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                // 迭代器遍历加载
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                try{
   
                    while(driversIterator.hasNext()) {
   
                        driversIterator.next();
                    }
                } catch(Throwable t) {
   
                }
                return null;
            }
        });
    }

    public static <S> ServiceLoader<S> load(Class<S> service) {
   
        // 获取当前线程上下文的类加载器ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
}
public static void main(String[] args) {
   
    // 打印线程上下文类加载器
    System.out.println(Thread.currentThread().getContextClassLoader());
}

结果输出

sun.misc.Launcher$AppClassLoader@18b4aac2

是的,正是我们的应用程序类加载器AppClassLoader。

简要回顾一下整体流程:

启动类加载器优先加载了DriverManager驱动管理类
DriverManager通过SPI机制加载jar包中对应的MySQL驱动
SPI机制通过线程上下文类加载器(AppClassLoader)完成加载MySQL驱动
看到这,我们发现DriverManager采用的SPI机制打破了双亲委派,其驱动的实现类由启动类加载器委托给应用程序类加载器去完成加载的。

但关于SPI机制是否真正打破了双亲委派,在网上似乎存在不同的理解,在这里结合自己的看法,总结一下:

从类加载的结果来说:并没有违背双亲委派,针对这里的两个jar包,rt.jar包中DriverManager依然由启动类加载器执行的加载,用户类路径classpath下mysql-connector-java.jar包中MySQL驱动则正常通过应用程序类加载器完成加载。

从类加载的过程来说:确实是违背了双亲委派机制,因为在执行过程中抛弃双亲委派加载流程,启动类加载器Bootstrap向下委托给应用程序类加载器AppClassLoader去完成加载的,逆向的委托了类加载器。

img

参考:https://blog.csdn.net/mm1274889792/article/details/138443721

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
42 3
|
6月前
|
Java 关系型数据库 MySQL
【JVM】JDBC案例打破双亲委派机制
【JVM】JDBC案例打破双亲委派机制
165 4
|
3月前
|
Java 编译器
什么是双亲委派机制?
什么是双亲委派机制?
223 59
|
3月前
|
前端开发 Java C++
双亲委派机制
这篇文章详细解释了Java中的双亲委派机制,包括其原理、类加载器的分类(启动类加载器、扩展类加载器、应用程序类加载器)以及它们之间的关系和作用。
|
6月前
|
Java
【JVM】双亲委派机制、打破双亲委派机制
【JVM】双亲委派机制、打破双亲委派机制
55 1
|
5月前
|
安全 前端开发 Java
程序与技术分享:1.2双亲委派机制及其原理
程序与技术分享:1.2双亲委派机制及其原理
|
6月前
|
监控 Java 关系型数据库
JVM工作原理与实战(十三):打破双亲委派机制-线程上下文类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、线程上下文类加载器等内容。
342 2
|
6月前
|
监控 安全 前端开发
JVM工作原理与实战(十二):打破双亲委派机制-自定义类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、自定义类加载器等内容。
78 1
|
Java 关系型数据库 MySQL
双亲委派机制,懂吧~ 那什么情况下需要破坏它,知道吗?
双亲委派机制,懂吧~ 那什么情况下需要破坏它,知道吗?
100 0
|
Java 应用服务中间件 API
打破双亲委派机制
打破双亲委派机制
57 0