Spring-JDBC 源码学习(3) —— DriverManager

DriverManager

上面提到 DataSource 获取连接(Connection) 的操作实质上将委托具体的 Driver 来提供 Connection 。有两种不同的方式,包括经由 DriverManager 遍历所有处于管理下的 Driver 尝试获取连接,或者在 DataSource 实例中直接声明一个特定的 Driver 来获取连接。

对于获取连接的具体操作,挖坑-待填。只描述简单的数据库供应商提供的 Driver 如何与 java 相联系。

在 DriverManager 中注册 Driver 实例

通常在与数据库交互逻辑的 Java 代码中,都会有 Class.forName("com.mysql.jdbc.Driver") (此处以 MySQL 提供的 mysql-connector-java-XXX.jar 为例,下同)的代码块,加载指定的 com.mysql.jdbc.Driver 为 java.lang.Class 类。

当然,在 JDBC 4.0 标准下,可以不必再显示声明 Class.forName("") 语句,Driver 也同样会在 DriverManager 初始化时自动注册。

// Class 类中对于 forName(String className) 的方法
// 作用为返回一个 java.lang.Class 实例。
public static Class<?> forName(String className) throws ClassNotFoundException {...}

同时, JVM 在加载类的过程中会执行类中的 static 代码块。下述 row 10 ~ 16 的代码片段将被执行。唯一的逻辑就是 new 一个 com.mysql.jdbc.Driver 实例,并将实例注册(registerDriver) 到 java.sql.DriverManager 中。

package com.mysql.jdbc;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    // ~ Static fields/initializers
    // ---------------------------------------------

    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    // ~ Constructors
    // -----------------------------------------------------------

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

下面再来看一下 DriverManager 中的 registerDriver() 方法。

public class DriverManager {

    // DriverManager 维护一个线程安全的 Driver 列表
    // 此处的 DriverInfo 里面即包装了 Driver 
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = 
        new CopyOnWriteArrayList<>();

    // 在 DriverManager 中注册 Driver
    public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {
        registerDriver(driver, null);
    }

    public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* 如果当前 Driver 不在列表中,即添加到列表。 */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }
}

通过 DriverManager 获取连接(Connection)

上一节有提到过可以通过 DriverManager 来遍历获取连接,也可以直接声明具体 Driver 并获取连接。下面代码展示的是通过 DriverManager 获取连接的操作​。 哈哈哈,反正最后都是由具体驱动实现获取连接。​

public class DriverManager {
    // 获取连接的 public 接口 (1)
    public static Connection getConnection(String url,
        java.util.Properties info) throws SQLException {
        return (getConnection(url, info, Reflection.getCallerClass()));
    }
    // 获取连接的 public 接口 (2)
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }
    // 获取连接的 public 接口 (3)
    public static Connection getConnection(String url)
        throws SQLException {
        java.util.Properties info = new java.util.Properties();
        return (getConnection(url, info, Reflection.getCallerClass()));
    }

    // 获取连接的内部逻辑实现
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) 
        throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }
        // url 是定位 DBMS 最重要的参数,不能为空
        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");

        // 遍历所有注册的 Driver ,并都尝试获取连接(Connection)
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // 判断注册的 Driver 是否由 ClassLoader callerCL 加载,不是则跳过
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    // 获取连接,:) 还是由 driver 实例自行提供
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + 
                                aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // 如果运行到下列代码,则表明获取连接失败,抛出错误
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }

}

简单的提一嘴,Connection 仍然只是一个针对 Java -> DB Server 的上层接口,如果想要更深层次地了解 Connection 与 DB Server 的交互,可以尝试去看一下 com.mysql.jdbc.MysqlIO 类,MySQL 实现的 JDBC4Connection 类也是在使用该类来实现对 DB Server 交互。(哈哈,只看过 MySQL 提供的 Driver 包)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值