JDBC Driver是如何自动注册的

在使用Java JDBC进行数据库编程时,我们引入驱动包之后,直接使用:
Connection connection = DriverManager.getConnection(url,user,password);便可以成功获得数据库连接,那么该方法到底做了哪些事情呢?

总结下来就是:

  1. 获取系统变量jdbc.drivers的值,该系统变量的值为java.sql.Driver实现类的全限定名称(多个使用:分隔),加载实现类(加载的时候自动将其注册到驱动管理器中);
  2. 通过SPI机制,扫描jar包下的META-INF/services/java.sql.Driver配置文件,配置文件中为java.sql.Driver实现类的全限定名称,加载该实现类(加载的时候自动将其注册到驱动管理器中)
  3. 遍历注册的驱动,使用给定的url尝试建立数据库连接并返回。

驱动管理器中被注册的驱动:

DriverManager.getConnection的源码如下:

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()));
}

//  真正执行获取数据库连接的方法,所有公开的getConnection方法都会最终都调用该方法
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.
     */
    //这里跟类的加载机制有关系,到JVM的时候再研究
    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    if (callerCL == null || callerCL == ClassLoader.getPlatformClassLoader()) {
        callerCL = Thread.currentThread().getContextClassLoader();
    }

    if (url == null) {
        throw new SQLException("The url cannot be null", "08001");
    }

    println("DriverManager.getConnection(\"" + url + "\")");
    // 重要方法,这个方法实现了对驱动的自动注册
    ensureDriversInitialized();

    // Walk through the loaded registeredDrivers attempting to make a connection.
    // Remember the first exception that gets raised so we can reraise it.
    SQLException reason = null;
	/*
	 * 遍历注册的驱动,依次使用url尝试建立连接
	 * 如果URL的格式不对,则返回null;
	 * 若URL为null,或URL格式正确,但连接数据库实现错误时,抛出 SQLException 异常
	 */
    for (DriverInfo aDriver : registeredDrivers) {
        // If the caller does not have permission to load the driver then
        // skip it.
        if (isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                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 we got here nobody could connect.
    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");
}


/*
 * Load the initial JDBC drivers by checking the System property
 * jdbc.drivers and then use the {@code ServiceLoader} mechanism
 * 根据系统参数:jdbc.drivers 以及SPI机制加载JDBC驱动
 */
private static void ensureDriversInitialized() {
    if (driversInitialized) {
        return;
    }

    synchronized (lockForInitDrivers) {
        if (driversInitialized) {
            return;
        }
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    /*
                     * 获取系统参数
                     * private static final String JDBC_DRIVERS_PROPERTY = "jdbc.drivers";
                     */ 
                    return System.getProperty(JDBC_DRIVERS_PROPERTY);
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
		// SPI 机制加载JDBC驱动
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
				// 这里可能类似于扫描,寻找哪些jar包中有MATE-INF/services/java.sql.Driver
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * 加载被扫描到的驱动,以便这些驱动可以被初始化
                 */
                try {
                    while (driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch (Throwable t) {
                    // Do nothing
                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers != null && !drivers.equals("")) {
            // 在使用系统参数注册驱动时,可以使用:分隔多个驱动
            String[] driversList = drivers.split(":");
            println("number of Drivers:" + driversList.length);
            for (String aDriver : driversList) {
                try {
                    println("DriverManager.Initialize: loading " + aDriver);
                    // 这里与类的加载机制有关,下面的语句不会实例化驱动,但驱动类中的静态初始化块会被执行,看看下一个代码块中的Mysql JDBC Driver的源代码,就知道这里做了什么了
                    Class.forName(aDriver, true,
                            ClassLoader.getSystemClassLoader());
                } catch (Exception ex) {
                    println("DriverManager.Initialize: load failed: " + ex);
                }
            }
        }

        driversInitialized = true;
        println("JDBC DriverManager initialized");
    }
}
package com.mysql.cj.jdbc;

import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            // 注册驱动
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

再看看DriverManager.registerDriver方法做了什么事情

public static void registerDriver(java.sql.Driver driver)
    throws SQLException {

    registerDriver(driver, null);
}
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
/*
 *	DriverAction: 注销驱动时,DriverManager.deregisterDriver会调用其deregister方法
 */
public static void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

    /* Register the driver if it has not already been added to our list */
    if (driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}

显式注册

通过上一节自动注册中源码,我们可以总结出多种显式注册方式:

  1. DriverManager.registerDriver(new Driver())
  2. Class.forName('com.mysql.cj.jdbc.Driver')
  3. System.setProperty('jdbc.drivers','com.mysql.cj.jdbc.Driver')
  4. java -Djdbc.drivers=com.mysql.cj.jdbc.Driver ProgramName
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值