2022.05.11 Druid源码阅读——为什么DruidDriver.getInstance

2022.05.11 Druid源码阅读——为什么DruidDriver.getInstance,第1张

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驱动。

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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/920281.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存