JDBC的驱动是如何加载的

注:本文出处:http://www.cnblogs.com/jiaoyiping/ 转载请保留出处

JDBC定义了一套接口,数据库产品的提供商会实现这些接口来提供自己的数据库驱动程序,这是个很好的面向接口编程的实例,想要替换数据库的时候只需要替换驱动程序就可以了(这里暂不考虑不同数据库之间的数据类型和SQL语法的差异)

那么针对具体的一款数据库(以PostgreSQL为例)是如何初始化的呢?

我们在使用原生的JDBC的时候都会写以下的代码:

Class.forName("org.postgresql.Driver");

Collection conn = DriverManager.getCollection("URL","username","password");

这两行代码就做了什么工作呢?

驱动又是如何加载的呢?我们知道,Class.forName()会导致类的初始化(

1.根据传入的类的完全限定名加载相应的class文件,

2.验证字节码并为类的静态域分配存储空间,

3.为静态属性设置值、执行静态代码块儿等 )

驱动的注册就是在静态代码块儿中执行的,以PostgreSQL9.3的Driver为例:

public class Driver implements java.sql.Driver
{

    // make these public so they can be used in setLogLevel below

    public static final int DEBUG = 2;
    public static final int INFO = 1;
    public static final int OFF = 0;
    
    private static final Logger logger = new Logger();
    private static boolean logLevelSet = false;
    private static Timer cancelTimer=null;

    static
    {
        try
        {
            // moved the registerDriver from the constructor to here
            // because some clients call the driver themselves (I know, as
            // my early jdbc work did - and that was based on other examples).
            // Placing it here, means that the driver is registered once only.
            java.sql.DriverManager.registerDriver(new Driver());
        }
        catch (SQLException e)
        {
            e.printStackTrace();
        }
    }

DriverManager的Register方法:

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

    }

DriverManager.getCollection()方法会遍历已经注册到系统中的驱动,调用驱动中相应的方法来得到真正的数据库连接。

    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.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection("" + url + "")");

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

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


}

自JDBC4.0开始,Class.forName("");可以省略掉了,因为在DriverManager的静态代码块儿里会寻找 jdbc.drivers 这个系统变量,找到相应的驱动程序并使用Class.forName()来加载它

详细代码如下:

/**
     * 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");
    }
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;
        }
        // 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() {

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> 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()) {
                        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);
            }
        }
    }
原文地址:https://www.cnblogs.com/jiaoyiping/p/5335361.html