栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

1.获得静态单例
public class DruidDriver implements Driver, DruidDriverMBean {
	private final static DruidDriver instance = new DruidDriver();
	
	public static DruidDriver getInstance() {
	    return instance;
	}
}
1.1 绕过安全机制,注册驱动

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

static {
    AccessController.doPrivileged(new PrivilegedAction() {
        @Override
        public Object run() {
            registerDriver(instance);
            return null;
        }
    });
}
 
2. 注册驱动 

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

    
    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()将负责加载这些驱动

 
 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() {
            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() {
			//此处通过SPI机制加载经过配置的java.sql.Driver接口
            ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator driversIterator = loadedDrivers.iterator();

            
            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();
        }
    }
}
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号