Tomcat JDBC池启动报ClassNotFoundException但可正常使用的疑问
问题分析与解决方案
为什么会出现这个现象
Tomcat的类加载器遵循双亲委派模型且存在层级隔离:
server.xml中配置的全局数据源属于Tomcat容器层面的资源,启动时由Tomcat系统类加载器加载,这个类加载器只能访问Tomcat安装目录下lib文件夹中的jar包。- 你把SQL Server驱动放在应用的
WEB-INF/lib下,这个目录属于应用类加载器的管辖范围,Tomcat系统类加载器启动时无法访问,所以会抛出ClassNotFoundException。 - 应用启动后,当代码获取连接池时,实际是通过应用类加载器加载驱动,此时能找到
WEB-INF/lib中的驱动,所以连接池可以正常工作。
这不属于Tomcat的“默认正常行为”,而是类加载隔离导致的矛盾——全局数据源本该由容器层管理,但你把依赖放在了应用层。
如何处理
1. 推荐的正确做法
将SQL Server驱动jar包复制到Tomcat安装目录的lib文件夹中。这样Tomcat系统类加载器在启动时就能找到驱动类,不会再抛出异常,同时全局数据源也能正常为所有需要的应用提供服务。
2. 若不想移动驱动位置,抑制异常的方法
把数据源配置从server.xml移到你的应用的META-INF/context.xml中。这样数据源变成应用级资源,由应用类加载器负责加载,Tomcat启动时不会再尝试用系统类加载器加载驱动,自然就不会出现启动报错,同时应用依然能正常使用连接池。
修改后的META-INF/context.xml配置示例:
<Context> <Resource auth="Container" type="javax.sql.DataSource" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/SABA" defaultAutoCommit="true" username="user" password="pass" url="jdbc:sqlserver://myserver:1433;DatabaseName=mydb;" /> </Context>
内容的提问来源于stack exchange,提问作者Alireza Fattahi
相关产品推荐
相关产品推荐

