aCoder2013/blog

初探JDBC源码

aCoder2013 opened this issue · 0 comments

JDBC Driver注册

JDBC的核心接口之一是java.sql.Driver,每一个驱动都必须提供实现类,那么它是怎样和DriverManager一起为我们提供数据库连接服务的呢,首先看一段经典的连接JDBC的代码.

//Class.forName("com.mysql.jdbc.Driver") JDBC4不加这行代码也可以
 Connection con = DriverManager.getConnection(
                         "jdbc:mysql:///test",
                         username,
                         password);

那么这段代码究竟做了什么神奇的操作呢,我们直接进去DriverManager这个类一看究竟,可以看到构造器是私有的,从而阻止我们去初始化这个类,JVM加载这个类之后首先会调用它的static代码段,
DriverManager的static段调用了loadInitialDrivers()方法。

    private DriverManager(){}

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
private static void loadInitialDrivers() {
        String drivers;
        //第一部分
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        //第二部分
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });
            
        println("DriverManager.initialize: jdbc.drivers = " + drivers);
        //第三部分
        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

第一部分

这段代码首先会去获取jdbc.drivers这个系统属性,可以通过

-Djdbc.drivers=com.mysql.jdbc.Driver

或者在代码中显示设置

System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver");

然后会通过ServiceLoader加载驱动程序,在JDBC4中,驱动程序必须在META-INF/services/包含java.sql.Driver这个文件,在其中包含数据库驱动的实现类,
比如Mysql的中包含的内容是:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

第二部分

然后会加载找到的所有驱动程序,这个时候会调用驱动程序的static代码块,我们去看一下Mysql和H2数据库的Driver实现类在其中做了什么操作:

public class com.mysql.jdbc.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 class org.h2.Driver implements java.sql.Driver {
     private static final Driver INSTANCE = new Driver();
     static {
         load();
     }
     public static synchronized Driver load() {
        try {
            if (!registered) {
                registered = true;
                DriverManager.registerDriver(INSTANCE);
            }
        } catch (SQLException e) {
            DbException.traceThrowable(e);
        }
        return INSTANCE;
    }

可以看到其中的共同点是都调用了DriverManager.registerDriver()去注册自己,其做的操作是将Driver封装成DriverInfo放到一个列表中保存。

    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    
    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 {

        /* 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);

    }

第三部分

如果判断drivers为空,说明没有通过jdbc.drivers找到驱动类,接着有没有通过ServiceLoader找到驱动类并初始化都无所谓了,直接返回即可,如果drivers不为空,则通过Class.forName()去加载驱动类,
调用static代码块去注册自己,同时通过String[] driversList = drivers.split(":");可以看出,指定jdbc.drivers指定驱动时可以给出多个,用:分隔。

获取连接

经过上面这段代码,DriverManager已经初始化完毕,各个驱动也已经注册完成,接着就调用getConnection()去获取连接,其核心代码也很容易理解,就是调用connect()方法去建立连接

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

        }

Flag Counter