12.jdbc第一步DriverManager

1.核心定位:JDBC 生态的 “驱动调度中枢”,用于管理数据库驱动实例。DriverManager 是 JDBC 核心工具类(java.sql.DriverManager)

  • MySQL 5.x(如 5.1.49):必须手动注册(原因:MySQL 5.x 驱动未实现 JDBC 4.0 的 SPI 规范,DriverManager 无法自动发现驱动类。) , 即手动加载类(Class.forName("com.mysql.jdbc.Driver");),然后 DriverManager 才能发现该驱动

    <!-- MySQL 5.x 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    
  • MySQL 8.0+(如 8.0.33):无需手动注册(原因:MySQL 8.0 驱动实现了 JDBC 4.0+ 的 SPI 规范(Service Provider Interface),DriverManager 会自动扫描并加载驱动。)

2.是只能管理数据库的驱动实例吗?其他的驱动不能管吗?

  • 明确结论:DriverManager 只管理 “符合 JDBC 规范的数据库驱动实例”(即实现了 java.sql.Driver 接口的实例,如 MySQL 的 com.mysql.cj.jdbc.Driver 、 Oracle的oracle.jdbc.driver.OracleDriver),不管理其他任何 “非 JDBC 驱动实例”。
  • DriverManager 的设计目标是 “为 JDBC 数据库连接提供统一的驱动管理”,其管理范围有严格边界。

从底层源码来认识DriverManager

1.核心存储:驱动元信息集合(registeredDrivers)

// 存储驱动元信息的核心集合,线程安全的写时复制容器
private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

存储元素:不是直接存储 Driver 实例,而是 DriverInfo(DriverManager 内部静态私有类),封装了 Driver 实例、类加载器、注销状态、权限等元信息; DriverInfo具体代码如下:

// DriverManager 内部静态私有类
private static class DriverInfo {
    // 核心:封装的 JDBC 驱动实例(实现了 java.sql.Driver 接口)
    final Driver driver;
    // 加载该驱动的类加载器(关键:用于类加载器适配和可见性判断)
    final ClassLoader classLoader;
    // 驱动的注销状态标记(默认 false:未注销;true:已注销)
    volatile boolean deregistered;
    // 驱动的权限对象(用于安全管理器的权限校验)
    private final SQLPermission permission;

    // 构造方法:创建 DriverInfo 时必须传入 Driver 实例和类加载器
    DriverInfo(Driver driver, ClassLoader classLoader) {
        this.driver = driver;
        this.classLoader = classLoader;
        this.deregistered = false;
        // 创建权限对象(用于后续 deregisterDriver 等操作的权限校验)
        this.permission = new SQLPermission("deregisterDriver");
    }
}

2.向 DriverManager 中注册驱动,即将 DriverInfo 加入集合

// 驱动注册方法:registerDriver 是外部触发入口(驱动静态代码块自动调用)
public static void registerDriver(Driver driver) throws SQLException {
    if (driver == null) {
        throw new NullPointerException("驱动实例不能为 null");
    }

    // 1. 获取当前线程的类加载器(用于封装到 DriverInfo)
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    // 2. 创建 DriverInfo 实例(封装驱动+类加载器等元信息)
    DriverInfo driverInfo = new DriverInfo(driver, classLoader);

    // 3. 核心操作:将 DriverInfo 加入 registeredDrivers 集合
    registeredDrivers.add(driverInfo);

    // 可选:打印调试日志(若开启日志)
    println("已注册驱动元信息:" + driverInfo.driver.getClass().getName());
}

3.获取连接核心源码 getConnection(String url, Properties info),遍历驱动器,找到能处理当前链接的驱动器,然后返回由由驱动创建的 Connection 对象,供上层使用。

private static Connection getConnection(String url, Properties info) throws SQLException {
    if (url == null || url.isEmpty()) {
        throw new SQLException("URL cannot be null or empty");
    }

    // 遍历所有已注册的驱动
    for (DriverInfo di : registeredDrivers) {
        if (di.deregistered) continue; // 跳过已注销的驱动

        try {
            // 筛选支持当前 URL 的驱动
            if (di.driver.acceptsURL(url)) {
                println("Trying to connect with driver: " + di.driver.getClass().getName());
                // 调用驱动的 connect 方法获取连接
                Connection conn = di.driver.connect(url, info);
                if (conn != null) {
                    println("Connection successful");
                    return conn;
                }
            }
        } catch (SQLException e) {
            println("Driver " + di.driver.getClass().getName() + " failed: " + e.getMessage());
            // 不中断,继续尝试下一个驱动
        }
    }

    // 所有驱动都失败,抛出异常
    throw new SQLException("No suitable driver found for " + url);
}

下一节重点讲解 Connection 对象

posted @ 2025-12-03 20:36  那就改变世界吧  阅读(7)  评论(0)    收藏  举报