在使用Java JDBC进行数据库编程时,我们引入驱动包之后,直接使用:
Connection connection = DriverManager.getConnection(url,user,password);
便可以成功获得数据库连接,那么该方法到底做了哪些事情呢?
总结下来就是:
- 获取系统变量
jdbc.drivers
的值,该系统变量的值为java.sql.Driver
实现类的全限定名称(多个使用:分隔),加载实现类(加载的时候自动将其注册到驱动管理器中); - 通过SPI机制,扫描jar包下的META-INF/services/java.sql.Driver配置文件,配置文件中为
java.sql.Driver
实现类的全限定名称,加载该实现类(加载的时候自动将其注册到驱动管理器中) - 遍历注册的驱动,使用给定的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);
}
显式注册
通过上一节自动注册中源码,我们可以总结出多种显式注册方式:
DriverManager.registerDriver(new Driver())
Class.forName('com.mysql.cj.jdbc.Driver')
System.setProperty('jdbc.drivers','com.mysql.cj.jdbc.Driver')
java -Djdbc.drivers=com.mysql.cj.jdbc.Driver ProgramName