前言
最近好久没发文章了,原因是AI太强了,随便问一句答案就有了,节约了很多折腾的时间,也就没法作为原创文章发布了,还有就是很多涉及公司内部的代码,没法公开发布。
这次遇到了一个项目,用的数据库是Sybase,需要使用jdbc进行连接然后查询其中的数据。这个项目以前在jdk8的环境运行是正常的,但是到了jdk17,加载驱动就出现问题了,这篇文章就来分析并解决这个问题。
问题现象
在jdk17中,加载Sybase驱动会出现递归调用,如图所示:
而jdk8就没有这个问题。
问题原因
原因出在jdk源码里,也就是jdk发生了变化,导致以前的Sybase驱动无法兼容新版本的jdk了(这不是废话吗)
jdk改动
具体代码是这样的:
jdk8的java.sql.DriverManager中的getDrivers方法长这样:
而jdk17长这样:
区别在于jdk17的getDrivers方法多了一行
ensureDriversInitialized();
这个方法是做什么的呢?答案是调用所有jdbc的驱动类的构造方法去初始化所有的驱动类,确保所有的驱动都被加载成功了。
上图代码是来自ensureDriversInitialized();方法中,注意看其中的注释,意思是加在这些驱动,让它们能够顺利的被实例化。
在driversIterator.next();中,会调用驱动自己的构造方法,来实例化它们。而在jdk8中则不会有这个操作。
Sybase驱动包
再看一下Sybase的驱动包,其中的com.sybase.jdbc4.jdbc.SybDriver这个类
其中会调用自己的registerWithDriverManager();这个方法,当我看到这个方法中终于知道了递归初始化的来源:
这个方法首先通过
DriverManager.registerDriver(this);
这个方法将驱动注册到内存中也就是registeredDrivers这个列表中。然后调用
DriverManager.getDrivers();
这个方法获取所有已注册的所有驱动,再通过循环判断Sybase驱动是否已经注册过了,如果注册过了则取消注册之前的Sybase驱动。这个代码原来(jdk8)看着是没有问题的。
但是到了jdk17中DriverManager.getDrivers();这个方法会去调用驱动类的构造方法去初始化驱动,然后构造方法中又会调用registerWithDriverManager(),其中的DriverManager.getDrivers();会调用构造方法,构造方法调用registerWithDriverManager()…………然后就发生递归了。然后这里的try里的catch中什么代码都没有,也不打印报错日志,导致什么错误都不显示了。这个驱动包写的确实让人一言难尽……
问题解决
既然问题找到了,那就得想办法解决这个问题,解决的方案有很多。
1、将jdk17换回jdk8
2、找其他版本兼容jdk17的驱动包
3、更换数据库
4、修改驱动包的字节码
解决方案1,这个想都没别想,目前项目的主题就是将jdk8换成jdk17。
解决方案3,这个也想都别想
更换Sybase其他版本的驱动包
官网
这是我第一个想到的解决方案,然后我在这个解决方案上面栽了一个大坑。Sybase目前被SAP收购了,变成了SAP ASE这个数据库,原来是www.sybase.com这个官网都打不开了,然后更坑的来了,我不好容易注册了SAP的账号,想要去下载东西了,结果一行不起眼的小字提醒了前来下载驱动的我
在SAP官网注册的用户都是P-user,而不是S-user,只有SAP的客户才能拥有S-user,拥有SAP官网的完整功能,难怪我折腾了半天,看了官网一堆的文档,就是下载不到任何东西!!!!
看看SAP官网这一行话吧,意思是只有S-user这个权限的用户才能使用下载功能,如果大家不信,可以去SAP官网试试,我是试了几个小时都没能够下载到任何东西。而老的sybase的官网已经打不开了,我看SAP的论坛里给的驱动链接都是老的Sybase官网的。
DBeaver
如此天才的我想到了DBeaver,这玩意每次要连数据库前不都会去下载驱动,说不定会有什么新的发现。然后我就试了一下,发现DBeaver不仅有Sybase还有SAP ASE
我悬着的心一下子就放下来了,结果等DBeaver下载完驱动,我就傻眼了。
怎么名称和我目前项目里用的一模一样,然后解压后打开其中的MANIDEST.MF,不能说是完全不同的,只能说是一模一样
还是那个2017年用jdk1.6编译出来的Sybase驱动包,完全不带任何更新的!!反编译后代码也是一模一样。当时把我给气得,这个DBeaver怎么连个驱动包都不更新一下,都2024年8月了,还在用2017年的驱动包。
修改字节码
只剩最后一条路了,修改字节码
javaassist
首先想到的是javaassist,说真的,这玩意我也是第一次用,java字节码的组成虽然以前自学过,但是过了太久已经忘了,使用javaassist需要将
Enumeration var2 = DriverManager.getDrivers();
连同后面的while循环全部删掉,这工作量就不小了,何况里面还有synchronized和try、catch块,不是字节码转换出错,就是字节码整完后无法加载,搞得我心累,压根就搞不定,问了AI,给我的答案也是错的,修改后的字节码根本无法使用。
然后同事给我支了一招,用idea中的jarEditor这个插件
jarEditor
在idea中搜索并安装这个插件,找到并加载Sybase的驱动包
上图通过maven加载项目本地的jar包
找到包中的SybDriver.class,点击下方的Jar Editor
将不需要的代码直接删掉
出现报错就解决报错,像我这里是实现com.sybase.jdbcx.SybDriver少了一个方法,直接补上去就行了,按F2就能定位报错的地方
Save后点击Build Jar就行了,Build成功后,这个Jar包就被彻底改变了,其中的字节码就是你刚改的。
然后使用这个新的驱动包后问题就解决了,Sybase驱动包加载的时候就不再递归了,连接一切正常。
小结
没想到一个Sybase加载驱动的问题解决了这么久,大概花了要4~5个小时,要不是同事给我推荐了Jar Editor,我恐怕要在javaassist的泥潭里继续挣扎,不过这javaassist应该是可以用的,只是我目前还不会,后面要好好研究一下这东西,目前Sybase驱动的问题算是解决了。