1. 引言

JDBC 是一套规范,定义了 Java 数据库连接(Java Database Connectivity)的 API 与 SPI 接口契约。其中,JDBC 驱动是连接数据库的入口抽象,是整个通信机制的核心。

本文将深入探讨 JDBC 驱动加载的几种方式,从早期的手动注册到现代的自动发现机制,帮助你在实际项目中避免踩坑,也能在面试时讲清楚底层原理。

2. JDBC 驱动的基本作用

要连接数据库,必须先获取一个 JDBC 驱动实例。这个实例由 DriverManager 管理,并通过 JDBC URL 来匹配对应的驱动。

一个典型的 JDBC URL 如下:

Connection con = DriverManager.getConnection(
   "jdbc:postgresql://localhost:21500/test?user=fred&password=secret&ssl=true");

这个 URL 包含了:

  • 数据库类型(如 postgresql
  • 主机地址与端口
  • 数据库名
  • 用户凭证及其他 vendor-specific 参数

✅ 关键点:DriverManager 是如何根据 URL 找到对应驱动的?

答案是:每个 JDBC 驱动在初始化时,都会向 DriverManager 注册自己,并声明它能处理哪些协议(如 jdbc:postgresql)。当调用 getConnection() 时,DriverManager 会遍历所有已注册的驱动,找到第一个能处理该 URL 的驱动并返回连接。

3. 传统方式:手动加载驱动(JDBC 4 之前)

在 JDBC 4(Java SE 6)之前,JVM 没有内置的服务发现机制。因此,必须手动触发驱动类的加载,常用方式是:

Class.forName("oracle.jdbc.driver.OracleDriver");

⚠️ 这行代码看似只是“加载类”,实则暗藏玄机 —— 它触发了类的静态初始化块,而驱动类的静态块中会完成注册逻辑。

以 PostgreSQL 驱动为例,其注册过程大致如下:

public static void register() throws SQLException {
    if (isRegistered()) {
        throw new IllegalStateException("Driver is already registered. It can only be registered once.");
    } else {
        Driver registeredDriver = new Driver();
        DriverManager.registerDriver(registeredDriver);
        Driver.registeredDriver = registeredDriver;
    }
}

这个 register() 方法通常在静态块中调用,确保类加载即完成注册。

✅ 可选优化:通过系统属性自动加载

即使在老版本中,也可以部分自动化这个过程。通过设置 jdbc.drivers 系统属性,DriverManager 会在初始化时自动尝试加载:

java -Djdbc.drivers=org.postgresql.Driver,com.mysql.cj.jdbc.Driver

这种方式虽然减少了代码侵入,但仍依赖启动参数,不够灵活。

❌ 踩坑提醒:
如果你用了 Class.forName() 但拼错了类名(比如 org.postgres.Driver),运行时会抛 ClassNotFoundException,连接直接失败。这类问题在容器化部署时尤其难排查。

4. 现代方式:SPI 自动发现(JDBC 4+)

从 Java 6 开始,JDK 引入了 Service Provider Interface (SPI) 机制,彻底解决了服务自动注册的问题。

核心原理

JDBC 驱动只需在 JAR 包中提供一个特殊文件:

META-INF/services/java.sql.Driver

该文件内容为驱动实现类的全限定名,例如:

org.postgresql.Driver

DriverManager.getConnection() 被调用时,JVM 会自动扫描 classpath 下所有 java.sql.Driver 文件,加载并注册这些驱动类。

✅ 实际效果

你现在完全可以**省略 Class.forName()**,只要驱动 JAR 在 classpath 中,就能自动识别。

比如你引入了:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.6.0</version>
</dependency>

Maven 会把 postgresql-42.6.0.jar 加入 classpath,其中已包含:

META-INF/services/java.sql.Driver
└── org.postgresql.Driver

启动时自动注册,无需任何额外代码。

⚠️ 注意事项

  • 即使你仍然写了 Class.forName(),也不会报错,只是多余。
  • 多个驱动共存时,DriverManager 会按注册顺序尝试匹配,直到成功。
  • SPI 机制也用于其他场景,如日志门面(SLF4J)、JAXP 等,是 Java 扩展机制的基石。

5. 总结

方式 是否需要 Class.forName() 适用版本 推荐程度
传统方式 ✅ 必须 JDBC 3 及以下 ❌ 已淘汰
SPI 自动发现 ❌ 不需要 JDBC 4+(Java 6+) ✅ 推荐

📌 核心结论:

  • 现代 Java 应用无需手动加载 JDBC 驱动,只要依赖正确引入,SPI 机制会自动完成注册。
  • 理解 SPI 原理有助于排查“找不到驱动”类问题,尤其是在模块化(JPMS)或自定义类加载器场景下。
  • 如果你在维护老系统,看到 Class.forName() 不用慌,那是历史遗留,但建议逐步清理。

示例代码已整理至 GitHub:https://github.com/baeldung/tutorials/tree/master/persistence-modules/core-java-persistence


原始标题:Loading JDBC Drivers | Baeldung