bigywater.tech
Java · 313

JDBC Driver SPI:驱动如何被发现和加载

为什么现在不用再写 Class.forName("com.mysql.jdbc.Driver")?DriverManager 怎么在一堆驱动里选一个?驱动从别的类加载器加载会怎样?这篇用四个真实的嵌入式数据库驱动和一个自己写的驱动逐项验证。

4 个
没有 Class.forName 时 DriverManager 自动发现的驱动数(20 ms)
08001
No suitable driver 的 SQLState
3 种
让自己写的驱动被注册的方式:Class.forName、-Djdbc.drivers、服务文件

服务文件

实测 · 本机执行输出
  h2-2.3.232.jar               java.sql.Driver 服务文件: ['org.h2.Driver']
  hsqldb-2.7.4.jar             java.sql.Driver 服务文件: ['org.hsqldb.jdbc.JDBCDriver']
  sqlite-jdbc-3.46.1.0.jar     java.sql.Driver 服务文件: ['org.sqlite.JDBC']
  derby-10.16.1.1.jar          java.sql.Driver 服务文件: ['org.apache.derby.iapi.jdbc.AutoloadedDriver']
  derbytools-10.16.1.1.jar     java.sql.Driver 服务文件: (没有)
  derbyshared-10.16.1.1.jar    java.sql.Driver 服务文件: (没有)

发现与选择

实测 · 本机执行输出
  1 没有任何 Class.forName,第一次调用 DriverManager.getDrivers():耗时 20 ms,发现 4 个驱动:[org.h2.Driver, org.hsqldb.jdbc.JDBCDriver, org.sqlite.JDBC, org.apache.derby.iapi.jdbc.AutoloadedDriver]
    jdbc:h2:mem:t → 驱动 org.h2.Driver;连接 H2 2.3.232 (2024-08-11);驱动版本 2.3;JDBC true
    jdbc:hsqldb:mem:t → 驱动 org.hsqldb.jdbc.JDBCDriver;连接 HSQL Database Engine 2.7.4;驱动版本 2.7;JDBC true
    jdbc:sqlite::memory: → 驱动 org.sqlite.JDBC;连接 SQLite 3.46.1;驱动版本 3.46;JDBC false
    jdbc:derby:memory:t;create=true → 驱动 org.apache.derby.iapi.jdbc.AutoloadedDriver;连接 Apache Derby 10.16.1.1 - (1901046);驱动版本 10.16;JDBC true
    jdbc:nonexistent:x → SQLException: No suitable driver (SQLState 08001)
    mysql://nojdbc → SQLException: No suitable driver (SQLState 08001)
  SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
  SLF4J: Defaulting to no-operation (NOP) logger implementation
  SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

尝试顺序

实测 · 本机执行输出
    org.h2.Driver                acceptsURL(jdbc:h2:mem:x)=true,acceptsURL(jdbc:sqlite::memory:)=false
    org.hsqldb.jdbc.JDBCDriver   acceptsURL(jdbc:h2:mem:x)=false,acceptsURL(jdbc:sqlite::memory:)=false
    org.sqlite.JDBC              acceptsURL(jdbc:h2:mem:x)=false,acceptsURL(jdbc:sqlite::memory:)=true
    org.apache.derby.iapi.jdbc.AutoloadedDriver acceptsURL(jdbc:h2:mem:x)=false,acceptsURL(jdbc:sqlite::memory:)=false
  DriverManager.getConnection("jdbc:sqlite::memory:")
      trying org.h2.Driver
      trying org.hsqldb.jdbc.JDBCDriver
      trying org.sqlite.JDBC
  getConnection returning org.sqlite.JDBC
  SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
  SLF4J: Defaulting to no-operation (NOP) logger implementation
  SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

注销与重新注册

实测 · 本机执行输出
  2 deregisterDriver(h2) 之后 getConnection(jdbc:h2:mem:y):No suitable driver found for jdbc:h2:mem:y (SQLState 08001)
    registerDriver 重新注册后:连接成功,H2
    不经 DriverManager,直接 driver.connect:成功
    driver.connect 遇到不认识的 URL 返回:null
  SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
  SLF4J: Defaulting to no-operation (NOP) logger implementation
  SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

不同的类加载器

实测 · 本机执行输出
  3 用独立的 URLClassLoader 加载 org.h2.Driver:加载器 URLClassLoader;DriverManager 已注册的驱动(按名字)[]
    DriverManager.getConnection:No suitable driver found for jdbc:h2:mem:l
    同一个驱动类手动实例化后直接 connect:true

自己的驱动

实测 · 本机执行输出
  [什么都不做]
      已注册驱动 [org.h2.Driver];getConnection(jdbc:my:x):No suitable driver found for jdbc:my:x
  [Class.forName("MyDriver")]
      [MyDriver 的静态初始化块执行了:已 registerDriver]
      已注册驱动 [MyDriver, org.h2.Driver];getConnection(jdbc:my:x) 成功,实际连接的是 H2
  [-Djdbc.drivers=MyDriver]
      [MyDriver 的静态初始化块执行了:已 registerDriver]
      已注册驱动 [org.h2.Driver, MyDriver];getConnection(jdbc:my:x) 成功,实际连接的是 H2
  [在 classpath 的 META-INF/services/java.sql.Driver 里登记 MyDriver]
      [MyDriver 的静态初始化块执行了:已 registerDriver]
      已注册驱动 [MyDriver, org.h2.Driver];getConnection(jdbc:my:x) 成功,实际连接的是 H2

分步演示

JDBC Driver SPI
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;H2 2.3.232、HSQLDB 2.7.4、sqlite-jdbc 3.46.1.0、Derby 10.16.1.1(都是嵌入式内存模式)
    • 没有验证:MySQL / PostgreSQL 等网络驱动(本文没有连接外部数据库)、连接池里的驱动加载、模块路径(module-info)下的 uses / provides java.sql.Driver、JDK 8 的行为、驱动在 Web 容器热部署时的泄漏(见类卸载一文)
    京ICP备2026067502号-1京公网安备11010502063487号