spi机制打破双亲委派机制

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
注册配置 MSE Nacos/ZooKeeper,182元/月
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
简介: 在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

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
前端开发 网络协议 Dubbo
超详细Netty入门,看这篇就够了!
本文主要讲述Netty框架的一些特性以及重要组件,希望看完之后能对Netty框架有一个比较直观的感受,希望能帮助读者快速入门Netty,减少一些弯路。
91285 32
超详细Netty入门,看这篇就够了!
|
10月前
|
消息中间件 中间件 Kafka
分布式事务最全详解 ,看这篇就够了!
本文详解分布式事务的一致性及实战解决方案,包括CAP理论、BASE理论及2PC、TCC、消息队列等常见方案,助你深入理解分布式系统的核心技术。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
分布式事务最全详解 ,看这篇就够了!
|
8月前
|
搜索推荐 数据挖掘 API
阿里巴巴API接口对电商的影响与收益
在全球电子商务快速发展的背景下,阿里巴巴作为领先的B2B平台,为中小企业提供商品批发、分销、供应链管理等一站式服务,并通过开放的API接口为开发者和电商企业提供数据资源与功能支持。本文将深入解析阿里巴巴API接口的功能(如商品搜索、详情、订单和用户管理)、应用(如商品展示、搜索优化、交易管理和用户行为分析)、收益(如流量增长、销售提升、库存优化)及实际案例,附带代码示例,助力电商从业者提升运营效率和用户体验。
313 0
|
缓存 Java 数据库
Spring Boot中使用监听器
系统的介绍了监听器原理,以及在 Spring Boot 中如何使用监听器,列举了监听器的三个常用的案例,有很好的实战意义。最后讲解了项目中如何自定义事件和监听器,并结合微服务中常见的场景,给出具体的代码模型,均能运用到实际项目中去,希望读者认真消化。
|
10月前
|
存储 SQL 数据库
Sybase数据恢复—Sybase数据库常见问题之数据库无法启动的恢复案例
Sybase数据库数据恢复环境: Sybase数据库版本:SQL Anywhere 8.0。 Sybase数据库故障&分析: Sybase数据库无法启动。 使用Sybase Central连接报错。 数据库数据恢复工程师经过检测,发现Sybase数据库出现故障的原因是:异常断电造成Sybase数据库无法回写正常数据,导致多个存储页数据不一致,系统表描述和存储表不一致,部分存储页底层数据完全杂乱。
|
监控 Java 关系型数据库
JVM工作原理与实战(十三):打破双亲委派机制-线程上下文类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、线程上下文类加载器等内容。
1202 2
|
11月前
|
安全 数据安全/隐私保护
如何使用贝锐花生壳DDNS,实现OA、ERP、CRM等办公系统远程访问?
在现代企业运营中,员工出差和分支机构的协同工作日益增多,确保不同地点员工安全便捷地访问公司内网的BS架构办公系统成为挑战。贝锐花生壳内网穿透服务提供了一种无需公网IP、无需复杂配置的解决方案,通过简单的三步设置,即可实现高效、安全的远程访问,支持多种主流办公系统。
323 10
|
Linux 测试技术 网络安全
SSH服务开机自动
【7月更文挑战第14天】
1164 5
|
物联网 API 网络性能优化
MQTT常见问题之没有权限查询如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总: