Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

初探JDBC源码 #5

Open
aCoder2013 opened this issue Aug 23, 2017 · 0 comments
Open

初探JDBC源码 #5

aCoder2013 opened this issue Aug 23, 2017 · 0 comments
Labels

Comments

@aCoder2013
Copy link
Owner

aCoder2013 commented Aug 23, 2017

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant