Tomcat Servlet访问GlobalNamingResources全局数据源异常排查
你遇到的java.sql.SQLException: Cannot create JDBC driver of class '' for connect URL 'null'报错,是配置缺失+代码逻辑时机错误共同导致的,按以下点逐一修复即可:
1. 缺失全局资源到应用的映射配置
定义在server.xml <GlobalNamingResources>节点下的资源是Tomcat容器全局级别的,默认不会对单个Web应用可见。你虽然在web.xml中配置了<resource-ref>声明要引用该数据源,但缺少核心的资源桥接配置,需要添加<ResourceLink>将全局资源映射到当前应用的JNDI上下文,否则应用的java:comp/env路径下根本不存在对应数据源的配置信息,自然读不到驱动类、连接URL等属性。
配置方式二选一即可:
- 单应用引用:在当前Web应用的
META-INF目录下新建context.xml,写入以下配置:
<Context> <ResourceLink name="jdbc/AuthenticationDatabase" global="jdbc/AuthenticationDatabase" type="javax.sql.DataSource" /> </Context>
- 全局生效:如果要让Tomcat下所有部署的应用都能引用这个数据源,可以把上述
<ResourceLink>配置写到Tomcat全局配置conf/context.xml里,或者server.xml对应虚拟主机的<Host>节点下。
2. JNDI查找时机错误
你当前把InitialContext初始化、DataSource查找的逻辑写在了Servlet类的成员变量定义位置,会在Servlet构造函数执行时运行,这个时间点Tomcat还没完成当前Web应用的JNDI上下文绑定,哪怕配置完全正确,这个时机查到的资源也会是空值。
需要把JNDI查找逻辑移到Servlet的init()生命周期方法中执行——init()方法是容器确认所有应用环境、资源就绪后才会调用的初始化方法。
修改对应代码:
首先删除类中成员位置的这三行初始化逻辑:
// 删除以下三行错误位置的初始化代码 Context initCtx = new InitialContext(); Context EnvCtz = (Context) initCtx.lookup("java:comp/env"); DataSource userDB = (DataSource) EnvCtz.lookup("jdbc/AuthenticationDatabase");
将userDB定义为类成员,在重写的init()方法中完成初始化:
private DataSource userDB; @Override public void init() throws ServletException { try { Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); userDB = (DataSource) envCtx.lookup("jdbc/AuthenticationDatabase"); } catch (NamingException e) { throw new ServletException("数据源初始化失败", e); } }
3. JDBC驱动包位置校验
由于你配置的是容器级别的全局数据源,DataSource是由Tomcat容器本身的类加载器加载的,因此MySQL JDBC驱动jar包必须放在Tomcat安装目录的lib文件夹下,不能只存放在应用自身的WEB-INF/lib目录中——受类加载器隔离机制限制,容器的类加载器无法读取Web应用私有目录下的jar包,会导致驱动类加载失败。
最后额外确认下server.xml中配置的数据库地址、账号、密码、库名与实际环境一致,本地MySQL服务正常运行即可。
内容的提问来源于stack exchange,提问作者Lars Dormans

