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

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


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

当在链接数据库执行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> 这个集合中去。

到这里驱动加载就结束了

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


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
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值