认识Java数据库驱动

本文主要探讨java.sql.DriverManager和java.sql.Driver类

背景:

我在实际项目中采用druid+mysql,简单代码如下:

    @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close")
    public DruidDataSource dataSource() throws Exception {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setName(MybatisHelp.name);
        druidDataSource.setUsername(MybatisHelp.username);
        druidDataSource.setPassword(MybatisHelp.password);
        druidDataSource.setUrl(MybatisHelp.url);
        druidSettings(druidDataSource);
        return druidDataSource;
    }

    public void druidSettings(DruidDataSource druidDataSource) throws Exception {
        druidDataSource.setMaxActive(28);
        druidDataSource.setInitialSize(2);
        druidDataSource.setUseUnfairLock(Boolean.TRUE);
        druidDataSource.setMinIdle(1);
        druidDataSource.setMaxWait(12000);
        druidDataSource.setPoolPreparedStatements(Boolean.FALSE);
        druidDataSource.setTestOnBorrow(Boolean.FALSE);
        druidDataSource.setTestOnReturn(Boolean.FALSE);
        druidDataSource.setTestWhileIdle(Boolean.TRUE);
        druidDataSource.setTimeBetweenEvictionRunsMillis(30000L);
        druidDataSource.setMinEvictableIdleTimeMillis(30000 * 4);
        druidDataSource.setRemoveAbandoned(Boolean.TRUE);
        druidDataSource.setRemoveAbandonedTimeout(180);
        druidDataSource.setLogAbandoned(Boolean.TRUE);
        druidDataSource.setFilters("stat");
        //每10分钟自动将监控的数据存储到日志中
        druidDataSource.setTimeBetweenLogStatsMillis(60000 * 60);
        druidDataSource.setDriver(new Driver());
    }

DriverManager

  • managing a set of JDBC drivers
  • JDBC 2.0 API, provides another way to connect to a data source. The use of a javax.sql.DataSource
  • Applications no longer need to explicitly load JDBC drivers using Class.forName(). Existing programs which currently load JDBC drivers using Class.forName() will continue to work without modification.
    /**
     * 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() {
                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() {
            public Void run() {

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

可以看到在DriverManager初始化的时候会加载JDBC drivers,属性来自于System.getProperty("jdbc.drivers"),并使用ServiceLoader机制(SPI,后续会讲解到)

断点进去会发现初始化代码走完发现driver是null,而我们的driver的真正生成是在实现的代码里面触发,如下:

  public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            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()
    }
}
    /**
     * Registers the given driver with the {@code DriverManager}.
     * A newly-loaded driver class should call
     * the method {@code registerDriver} to make itself
     * known to the {@code DriverManager}. If the driver is currently
     * registered, no action is taken.
     *
     * @param driver the new JDBC Driver that is to be registered with the
     *               {@code DriverManager}
     * @exception SQLException if a database access error occurs
     * @exception NullPointerException if {@code driver} is null
     */
    public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {

        registerDriver(driver, null);
    }

此处由实现类触发,这里的例子通过druid配置mysql

认识Java数据库驱动_第1张图片
image.png
/**
     * Registers the given driver with the {@code DriverManager}.
     * A newly-loaded driver class should call
     * the method {@code registerDriver} to make itself
     * known to the {@code DriverManager}. If the driver is currently
     * registered, no action is taken.
     *
     * @param driver the new JDBC Driver that is to be registered with the
     *               {@code DriverManager}
     * @param da     the {@code DriverAction} implementation to be used when
     *               {@code DriverManager#deregisterDriver} is called
     * @exception SQLException if a database access error occurs
     * @exception NullPointerException if {@code driver} is null
     * @since 1.8
     */
    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);

    }

加载完后:


image.png

SPI

首先贴上mysql包的扩展内容


认识Java数据库驱动_第2张图片
image.png

SPI的相关内容推荐
奶芋的 文章:https://mp.weixin.qq.com/s/8rrHhgUn4VxEgBlw_Oe-qQ
http://www.cnblogs.com/zhongkaiuu/articles/5040971.html

你可能感兴趣的:(认识Java数据库驱动)