使用JDBC驱动时什么时候需要指定Class.forName(driverName)?
JDBC驱动加载核心前提
- JDBC 3.0版本的驱动没有实现SPI自动注册规范,驱动类内部通过静态代码块完成注册:当驱动类被类加载器加载时,静态代码块会自动执行
DriverManager.registerDriver(),把当前驱动实例注册到JDBC的驱动管理器中。 - 你使用的JTDS 1.3.1属于JDBC 3.0驱动,本身不包含
META-INF/services/java.sql.Driver的SPI配置文件,所以不会被JDK原生的JDBC 4.0自动加载逻辑识别。 - Tomcat采用类加载优先级倒置的机制:每个Web应用的类加载器会优先加载自身
WEB-INF/lib目录下的类,加载不到才会委托父加载器(负责加载Tomcat全局lib目录的Common类加载器)加载。 - JDK自带的
DriverManager由启动类加载器加载,它的驱动匹配逻辑会校验驱动对应的类加载器是否对当前调用方可见,只有可见的驱动才会被用于连接创建。
问题1:两份jar包共存时,为什么Class.forName(driverName).newInstance()能解决报错
你遇到的报错本质是DriverManager中没有注册对应JTDS驱动的实例:
- 因为JTDS 1.3.1不支持SPI自动注册,没有显式加载逻辑的前提下,驱动类不会被加载,也就不会触发静态代码块的注册逻辑,所以
DriverManager识别不到可用驱动。 - 调用
Class.forName(driverName)时,会使用当前线程的上下文类加载器(也就是当前Web应用的类加载器)去加载驱动类,触发静态代码块执行完成驱动注册,自然就能正常创建连接。
问题2:两份完全相同的jar包共存时,程序如何选择生效的jar包
完全遵循Tomcat的类加载优先级规则:
- 默认情况下,Web应用的类加载器会优先读取自身
WEB-INF/lib下的JTDS jar包加载驱动类,所以此时生效的是Web应用内部的jar包。 - 只有Web应用
WEB-INF/lib下没有对应jar包时,才会委托父加载器加载Tomcat全局lib目录下的JTDS jar包。
问题3:移除Web应用lib下的JTDS jar包后,为什么非JDBC4.0的驱动也能被自动识别
这是Tomcat自身的特殊处理逻辑,和JDK的JDBC自动加载无关:
- Tomcat启动时,会扫描全局
lib目录下的JDBC驱动类,主动触发驱动类的加载和注册,不需要应用层手动调用Class.forName。 - 全局
lib下的类由Common类加载器加载,是所有Web应用类加载器的父加载器,所以注册到DriverManager的驱动实例对所有Web应用都可见,你在代码里直接调用DriverManager.getConnection就能匹配到已经注册的JTDS驱动。
内容的提问来源于stack exchange,提问作者jabe
相关产品推荐
相关产品推荐

