自定义类加载器动态加载 JDBC 驱动

我们可以用自定义的 URLClassLoader 从外部动态加载类,并使用它。但数据库驱动的管理类 DriverManager 却不比较苛刻,不承认非当前应用系统加载器加载的驱动类。见 DriverManager 的 JavaDoc 

When the method getConnection is called, the DriverManager will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application

对于有有应用自定义类加载器加载数据库驱动类的需求时,就要对原 Driver 简单包装一下。继续往后会说介绍为什么要这么做。

说明一下,DriverManager 能够根据 JDBC 连接字符串匹配到驱动类,所以一般来说都不需要显式调用 DriverManager.registerDriver() 方法。

先看 DriverManager 在应用外部驱动类时会出现什么情况

上面的代码在执行最后一行获取数据库连接时报出的异常是

java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/test?useSSL=false

但是能正确用自定义的类加载器加载到驱动类 com.mysql.jdbc.Driver, 否则会报出 ClassNotFound 的异常。把上面代码中的

Class.forName(driverClass, true, classLoader)

改成

Driver driver = (Driver) Class.forName(driverClass, true, classLoader).newInstance();
DriverManager.registerDriver(driver);

也无济于事。但是只要是系统加载器的数据库驱动就没问题,下面执行命令正常

java -cp ~/drivers/mysql-connector-java-5.1.43.jar cc.unmi.JdbcDriverLoader

到底发生了什么呢?还是那个  DriverManager, 进到它的 getConnection(....) 方法,有兴趣的可以去阅读 DriverManager 类的源代码,这里不细究的,简单来讲就是

在获取连接时,DriverManager 会用加载 cc.unmi.JdbcDriverLoader 类的加载器(Launcher$AppClassLoader)检验一下是否能加载到数据库驱动类,显然这里要卡壳了。com.mysql.jdbc.Driver 对于应用程序类加载器是不可能见的,所以报出驱动找不到的异常。

为了解决能动态的加载外部数据库驱动,我们需要引入下面那个 DriverShim 包装类

然后加从外部加载驱动时应该是 DriverShim 包装类型

这样就行了,可以正确找到 "com.mysql.jdbc.Driver", 为什么如此简单的包装就把骄傲的 DriverManager 给骗了呢?就这么简单。

使用了 DriverShim 包装类后,在 getConnection() 时 DriverManager 同样要验证驱动是否对应用程序类加载器可见,只是这时候要验证的是这个 DriverShim 而非通过自定义类加载器弄进来的 "com.mysql.jdbc.Driver" 了。而后在使用 DriverShim 桥接到实际的 com.mysql.jdbc.Driver 时 DriverManager 就管不着了。

大致意思就是:你 DriverManager 不是想验证数据库驱动是否是应用程序类加载器加载的吗?给个壳逗你玩一下,我在壳里面呢,你管我是由哪个类加载器加载的。

链接:Pick your JDBC driver at runtime

本文链接 https://yanbin.blog/custom-classload-dynamic-load-jdbc-driver/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

6 Comments
Inline Feedbacks
View all comments
trackback

[…] 先看 DriverManager 在应用外部驱动类时会出现什么情况 阅读全文 >> […]

hzy
hzy
2 years ago

这个思路不错。

路人甲
路人甲
3 years ago

成功解决了我的问题,赞啊

Kjhl
Kjhl
3 years ago

厉害了

trackback

[…] 先看 DriverManager 在应用外部驱动类时会出现什么情况 阅读全文 >> […]

Jack
6 years ago

嗯,这个有点意思。我前面做ANDROID开发时,想连接一下MYSQL库,结果居然没有办法。要加载的东西太多。有了你这篇文章,我倒是可以试一下看能否搞定了。