关于为什么有SPI后Mybatis中还是要指定数据源驱动类名

最近和室友聊到SPI这个机制,告诉他其实没必要写Class.forName(“驱动类名”) 这段代码,结果他说MyBatis必须在配置文件指定数据库驱动,不然就会报错。
我们知道,SPI机制让jdbc没必要再指定驱动位置,对应的数据库驱动jar包会在META-INF/services/服务接口名文件中指定驱动位置,所以按道理来说MyBatis中没必要再指定驱动位置了,这就非常矛盾。
于是去探究了一番,有了这篇博客。

复现现场

注释掉mybatis配置文件中的驱动类,然后连接到数据库,会发现如下错误:

异常栈按FILO的顺序打印,最下面的异常最早被抛出,导致上面的异常抛出。所以主要关注下面这个异常的路径。路径按FIFO的顺序打印,最上面的路径最先被异常经过,随后逐渐向外抛出。所以看最下面这个异常的第一行,看看这个异常最开始是从哪来的。可以发现是map集合的get方法抛出来空指针异常,说明是传入的参数是null。继续观察是哪里调用了这个集合方法,关注到第三、四行,即红色框框部分,initializeDriverdoGetConnection两个方法。

探究源码

registeredDrivers集合

在进入这个方法的源码之前,我们需要先了解发生异常的这个类中的一个成员变量registeredDrivers,由名字不难猜出这个变量存储了所有注册的驱动,查看源码,也确实如此:

initializeDriver方法

进入initializeDriver方法,可以发现就是registeredDrivers这个成员变量集合抛出的异常。通过在initializeDriver这个方法断点调试可知,这里的driver指的就是我们在配置文件中写的driver,由于注释文件中的driver被我们注释掉了,所以这里自然就为null,所以就抛出了空指针异常。

为什么抛出异常弄清楚了,再来看看这个方法到底干了什么,从这个方法的名字我们首先可以猜测,它做的应该是初始化driver的活。

在方法开始处,先判断注册的驱动中是否有我们配置文件中填写的driver驱动,没有的话就根据我们配置文件中填写的driver驱动的位置进行初始化,并把这个新注册的驱动加入到registeredDrivers集合中。然后就到了doGetConnection方法(注意是doGetConnection这个方法先调用了initializeDriver方法,然后再获取数据库连接),一切都很符合逻辑。

但问题为什么MyBatis不直接获取数据库连接,而是先要检查一下我们配置文件中的数据库驱动是否有被注册,很明显SPI机制会帮我们自动注册,这怎么都感觉是多此一举,断点调式到这个地方,可以发现即使没有指定数据库驱动,registeredDrivers集合也不为空:

=
MySQLjar包中的文件

可以发现SPI机制确实有帮我们自动注册到,所以MyBatis这一步确实像是多此一举,还导致我们必须得在配置文件中指定对应数据源驱动的全限定类名。

分析原因

去查了一下,JDK6,也就是SPI机制,在2006年就发布了,而MyBatis是在2010年才出的,而MyBatis的前身iBatis,2001年就出了,所以MyBatis无视SPI机制,自己去加载,可能是由于出得太早。

但MyBatis接收维护iBatis后依然没改,感觉还是比较奇怪,可能是MyBatis觉得有些数据库驱动没迎合SPI机制,而自己作为一个框架什么情况都得考虑到,做到尽可能通用,所以还是自己去手动加载吧。

Spring Boot中的情况

使用Spring Boot时,即使没有指定驱动名但是依然可以启动,查看源码,我们可以发现其实是因为Spring Boot是根据我们写的url来判断对应的驱动名:

猜你喜欢

转载自blog.csdn.net/weixin_55658418/article/details/129309506