2022.05.11 Druid源码阅读——为什么DruidDriver.getInstance()会加载其他驱动?

1.获得静态单例

public class DruidDriver implements Driver, DruidDriverMBean {
	private final static DruidDriver instance = new DruidDriver();
	/**
	 * 此方法将返回在DruidDriver中所定义的单例
	 * @return
	 */
	public static DruidDriver getInstance() {
	    return instance;
	}
}

1.1 绕过安全机制,注册驱动

该方法所返回的是DruidDriver的静态单例,在静态代码块中可以找到以下代码,通过调用AccessController.doPrivileged()方法绕过安全检查,调用本类中的注册驱动方法对当前实例进行注册。

static {
    AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            registerDriver(instance);
            return null;
        }
    });
}

2. 注册驱动

由于DruidDriver实现了java.sql.Driver接口,自然也允许自身被当做驱动由驱动管理器进行注册。

    /**
     * 注册驱动
     * @param driver
     * @return
     */
    public static boolean registerDriver(Driver driver) {
        try {
            //利用驱动管理器进行注册驱动
            DriverManager.registerDriver(driver);

            try {
                //创建MBeanServer(JMX相关内容)
                MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
                //新建MBean ObjectName, 在MBeanServer里标识注册的MBean
                ObjectName objectName = new ObjectName(MBEAN_NAME);
                //如果MBean中没有注册过当前ObjectName,则将当前实例注入至MBean中
                if (!mbeanServer.isRegistered(objectName)) {
                    mbeanServer.registerMBean(instance, objectName);
                }
            } catch (Throwable ex) {
                if (LOG == null) {
                    LOG = LogFactory.getLog(DruidDriver.class);
                }
                LOG.warn("register druid-driver mbean error", ex);
            }

            return true;
        } catch (Exception e) {
            if (LOG == null) {
                LOG = LogFactory.getLog(DruidDriver.class);
            }

            LOG.error("registerDriver error", e);
        }

        return false;
    }

3.DriverManager注册驱动

DriverManager的静态代码块中存在如下定义,loadInitialDrivers()将负责加载这些驱动

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

通过查阅DriverManager的文档注释以及loadInitialDrivers()方法可知,在JDBC4.0驱动以后,可以通过META-INF/services/java.sql.Driver文件指定由SPI机制加载的JDBC驱动。

DriverManager4.0针对数据库驱动的增强

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() {
			//此处通过SPI机制加载经过配置的java.sql.Driver接口
            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);
        }
    }
}

4.Druid是否还会加载其他的驱动

在DruidDataSource的初始化方法中,除了DruidDriver.getInstance(),还存在resolveDriver()方法可以加载数据库驱动,此方法将通过所配置的driverClassName加载驱动,如果未配置,则会根据url自动识别dbType,然后选择相应的driverClassName

通过此处加载的驱动,与DriverManager利用SPI机制加载的对象不一致

protected void resolveDriver() throws SQLException {
    //如果当前连接池没有指向驱动对象,则动态加载驱动
    if (this.driver == null) {
        //如果当前没有指定驱动,则会根据所配置的jdbcUrl进行匹配,
        if (this.driverClass == null || this.driverClass.isEmpty()) {
            //此处的匹配规则是通过jdbcUrl开头的标记进行判断,当他符合某一驱动标记时,则返回对应的驱动
            this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl);
        }
        //由于Druid内置了一个Mock驱动,此处是为Mock驱动提供支持
        if (MockDriver.class.getName().equals(driverClass)) {
            driver = MockDriver.instance;
        } else if ("com.alibaba.druid.support.clickhouse.BalancedClickhouseDriver".equals(driverClass)) {
            //此处为Druid内置的ClickHouse负载均衡驱动
            Properties info = new Properties();
            info.put("user", username);
            info.put("password", password);
            info.putAll(connectProperties);
            driver = new BalancedClickhouseDriver(jdbcUrl, info);
        } else {
            if (jdbcUrl == null && (driverClass == null || driverClass.length() == 0)) {
                throw new SQLException("url not set");
            }
            //此时通过类加载器与指定的驱动进行匹配,如果driverClassLoader为空,则取当前线程的ContextClassLoader
            driver = JdbcUtils.createDriver(driverClassLoader, driverClass);
        }
    } else {
        //如果有加载驱动,但是为配置驱动名,则从驱动中进行获取
        if (this.driverClass == null) {
            this.driverClass = driver.getClass().getName();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值