• 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);
                }
            }
        }
  • 相关阅读:
    Lost connection to MySQL server at 'waiting for initial communication packet', system error: 0
    Can't connect to MySQL server on '192.168.7.175' (10060)
    单精度浮点数(float)加法计算出错
    当前不会命中断点 还没有为该文档加载任何符号
    64位程序,long*转long 出错
    当前安全设置不允许下载该文件的原因以及图文解决办法
    IndentationError: unindent does not match any outer indentation level
    MongoDB状态查询:db.serverStatus()
    bson.errors.InvalidStringData: strings in documents must be valid UTF-8
    Transformer的PyTorch实现
  • 原文地址:https://www.cnblogs.com/jiaoyiping/p/5335361.html
Copyright © 2020-2023  润新知