spi机制打破双亲委派机制

简介: 在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

相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
4月前
|
人工智能 JSON 数据挖掘
大模型应用开发中MCP与Function Call的关系与区别
MCP与Function Call是大模型应用的两大关键技术。前者是跨模型、标准化的通信协议,实现多工具动态集成;后者是模型调用外部函数的内置机制。MCP如同“蓝牙协议”,支持多设备互联互通,具备高兼容性与扩展性;Function Call则像“语音助手”,依赖特定模型完成具体任务。二者在功能上互补:MCP构建通用接口层,解耦模型与工具;Function Call负责意图解析与指令生成。
|
消息中间件 中间件 Kafka
分布式事务最全详解 ,看这篇就够了!
本文详解分布式事务的一致性及实战解决方案,包括CAP理论、BASE理论及2PC、TCC、消息队列等常见方案,助你深入理解分布式系统的核心技术。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
分布式事务最全详解 ,看这篇就够了!
|
Java 编译器 Spring
面试突击78:@Autowired 和 @Resource 有什么区别?
面试突击78:@Autowired 和 @Resource 有什么区别?
17459 7
|
数据采集 Web App开发 数据可视化
Python爬虫教程:Selenium可视化爬虫的快速入门
Python爬虫教程:Selenium可视化爬虫的快速入门
|
监控 NoSQL Java
Spring Boot Actuator 使用和常用配置
Spring Boot Actuator 使用和常用配置
2059 5
|
NoSQL 关系型数据库 分布式数据库
凭安征信携手阿里云PolarDB和MongoDB,挖掘信用背后的数据金矿
PolarDB和MongoDB共同支撑凭安征信的全量数据需求
|
SQL 算法 Java
(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!
前面《MySQL主从原理篇》、《MySQL主从实践篇》两章中聊明白了MySQL主备读写分离、多主多写热备等方案,但如果这些高可用架构依旧无法满足业务规模,或业务增长的需要,此时就需要考虑选用分库分表架构。
7148 4
|
Linux 测试技术 网络安全
SSH服务开机自动
【7月更文挑战第14天】
1965 5
|
前端开发 搜索推荐 开发者
前端基础(三)_路径(绝对路径、相对路径)、语义化、特殊字符
本文介绍了前端开发中的路径概念(包括绝对路径和相对路径)、HTML的语义化以及特殊字符的使用。文章解释了绝对路径和相对路径的区别和应用场景,阐述了HTML语义化的意义和好处,并通过示例代码展示了如何在HTML中使用特殊字符。
1684 0