mysql jdbc驱动源码分析(驱动加载)

时间:2022-06-21 06:28:10

jdbc链接数据库的时候我们知道有如下几个步骤:

1、加载驱动

2、获取数据库链接Connection

3、获取statement、preparedstatement

4、执行查询、更新语句获取结果ResultSet

5、调用ResultSet结果集实例的方法来获取数据

6、释放资源


第一篇我们就来看看驱动以及加载驱动:

我们刚开始学的时候会有这样的疑问,什么是驱动驱动是干什么的,什么叫加载驱动,为什么要加载驱动。

一、什么是驱动:

1、平时我们会说mysql数据库的驱动,oracle数据库的驱动,其实是要链接数据库我们就用到了驱动,在java 开发中我们要导入一个jar包才能使用jdbc这个技术和对应的数据库才能连接,而导入的这个jar包就是所谓的驱动。

2、大家都会问jdbc不是java技术吗,其实jdbc是为了链接数据库java定义的一些规则,即也就是java给各个数据库开发商提供了一些规则(接口)。驱动就是各个厂商对jdbc这个规则的具体实现。

3、java定义规则的好处是java技术人员只要熟悉了java jdbc的规则就可以连接所有的数据库而不是学习一种数据库要学习一次,所以jdbc的好处就在这里。

4、我们可以在 java.sql 这个包中看到很多接口如:Driver、statement、preparedStatement 等而具体实现是在各个数据库的驱动中。

我们知道了jdbc和驱动下面我们就来看看连接数据库的一步:加载驱动。

在加载的时候使用两种反射可以来获取Class的实例:  class.forName() 和 object.class两种方式。

下面就来看看 jdbc 加载驱动的源码:

二、驱动源码分析

1、com.mysql.jdbc.Driver类

 //实现java提供的接口,其实接口就是一种规则、协议
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// Register ourselves with the DriverManager
// 静态代码块
static {
try {
// 注册mysql实现的驱动类
 java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* 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()
}
}
2、当我们链接数据库的时候都会使用Class.forName("com.mysql.jdbc.Driver"),Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。这样也就是执行了com.mysql.jdbc.Driver类的静态代码块即注册mysql实现的驱动类。

3、java.sql.DriverManager的registerDriver(java.sql.Driver driver)方法如下:

  /**
* Registers the given driver with the <code>DriverManager</code>.
* A newly-loaded driver class should call
* the method <code>registerDriver</code> to make itself
* known to the <code>DriverManager</code>.
*
* @param driver the new JDBC Driver that is to be registered with the
* <code>DriverManager</code>
* @exception SQLException if a database access error occurs
*/
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {

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

println("registerDriver: " + driver);

}
(1)、如果这个驱动还没有添加到集合中则加入集合,而其中的DriverInfo是一个内部类具体代码如下:

class DriverInfo {

final Driver driver;
DriverInfo(Driver driver) {
this.driver = driver;
}

public boolean equals(Object other) {
return (other instanceof DriverInfo)
&& this.driver == ((DriverInfo) other).driver;
}

public int hashCode() {
return driver.hashCode();
}

public String toString() {
return ("driver[className=" + driver + "]");
}
}

从中我们看到只有一个变量就是Driver而且被final修饰,并且重写了equals 、hashCode、和toString 三个方法。这个类的作用就是判断是不是同一个对象。== 判断引用的对象地址或句柄地址是否相等。

(2)、当创建了一个DriverInfo 对象后,将这个类添加到集合 registeredDrivers中,这个集合的具体类型如下:

   private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();

CopyOnWriteArrayList集合实现了List集合,他是<a target=_blank title="java.util 中的类"><code>ArrayList</code></a> 的一个线程安全的变体,其中所有可变操作(<tt>add</tt>、<tt>set</tt> 等等)都是通过对底层数组进行一次新的复制来实现的。
而他的<code><strong><a>addIfAbsent</a></strong>(<a target=_blank title="CopyOnWriteArrayList 中的类型参数">E</a> e)</code>方法就是如果不存在则添加到集合中。

所以当Driver的static{} 代码块中调用DriverManager类的register就是向这个集合中添加了那个驱动类。

注意:这里的CopyOnWriteArrayList和ArrayList都实现了List这个接口说以方法什么使用方式相同,只是底层的实现不同。
到这里我们知道了DriverManager.register(Driver  driver)方法就是将驱动类的实例添加到了这个集合中,但是在执行register方法之前首先会执行DriverManager类的静态代码块。 静态代码块中的代码如下:


 /**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
// 静态代码块 来实现 驱动类的初始化
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
静态代码块中调用了 loadInitialDrivers();  方法,方法代码如下:

   // 加载初始化驱动
private static void loadInitialDrivers() {
String drivers;

try {
// AccessController类的doPrivileged()方法调用了PrivilegedAction实例的run方法,run返回的值就是AccessController类的doPrivileged()方法返回的值
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
// 方法就是从系统属性中获取key 为jdbc.drivers的values 但之前还没设置key 为jdbc.drivers的值所以这里返回的是null
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()

AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 针对给定服务类型创建新的服务加载器,使用当前线程的上下文类加载器。
// 这里执行的结果是会加载两个驱动 一个是:sun.jdbc.odbc.JdbcOdbcDriver@256e9494 一个是:com.mysql.jdbc.Driver@58a40787
<span style="color:#CC0000;"> // 将给定的类相关的类都加载到jvm内存中</span>
// 之后会附上执行代码和结果
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
// 以延迟方式加载此加载器服务的可用提供者。
Iterator driversIterator = loadedDrivers.iterator();

/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
println(" Loading done by the java.util.ServiceLoader : "+driversIterator.next());
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});

println("DriverManager.initialize: jdbc.drivers = " + drivers);
// 从这个if 判断看,如果没有通过System.getProperty("jdbc.driver");获取driver 则直接退出初始化

if (drivers == null || drivers.equals("")) {
return;
}
// 驱动列表,这里的驱动是odbc
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
// 使用指定的类加载器来加载获取的drivers驱动
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}

到这里 这个初始化方法就结束了,通过上面的分析和跟进我们发现这个初始化方法除了使用 ServiceLoader<Driver> 类来记载指定的方法到jvm内存中外没有做任何的事情。


这里附上ServiceLoader类加载odbc jdbc驱动的代码以及执行结果:

//一个简单的服务提供者加载设施。 
public static Void test2(){
//针对给定服务类型创建新的服务加载器,使用当前线程的上下文类加载器。
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
System.out.println(" Loading done by the java.util.ServiceLoader : "+driversIterator.next());
}
} catch(Throwable t) {
// Do nothing
}
return null;
}

mysql jdbc驱动源码分析(驱动加载)

到这里驱动的加载也就完了,下面总结一下整个过程:

当在链接数据库执行Class.forName("com.mysql.jdbc.Driver"); 这就是使用了java的反射来动态加载类,当加载这个类到jvm内存的时候会执行com.mysql.jdbc.Driver类的静态代码块即 static{} 而 静态代码块中是   java.sql.DriverManager.registerDriver(new Driver()); 这时有两步:

(1)、将java.sql.DriverManager这个类加载到jvm内存而这时和上面的一样还是会首先执行java.sql.DriverManager类的静态代码块(static{})而静态代码块中调用了loadInitialDrivers(); 加载初始化驱动的方法。

这个方法的具体过程在上面的代码中有分享,我们发现除了使用 ServiceLoader<Driver> 来加载指定类之外就没做什么。

(2)、静态代码块执行完之后就要执行java.sql.DriverManager.registerDriver(new Driver());类的注册方法了,具体方法内容上面分析过,他就是把这个驱动Driver类的实例添加到CopyOnWriteArrayList<T> 这个集合中去。

到这里驱动加载就结束了

其中有红色标记的地方不是很确定 如果大家清楚还请赐教 谢谢!


菜鸟一枚如有理解错误还请指正 谢谢!