Tomcat使用线程的上下文类加载器确定要执行查找的JNDI上下文。如果线程上下文类加载器不是Web应用程序类加载器,则JNDI上下文为空,因此查找失败。
问题在于,DataSource
启动期间执行的JNDI查找是在主线程上执行的,而主线程的TCCL不是Tomcat的Web应用程序类加载器。您可以通过更新TomcatEmbeddedServletContainerFactory
bean来设置线程上下文类加载器来解决此问题。我还没有说服自己,这不是一个可怕的骇客,但确实有效……
这是更新的bean:
@Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
@Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
tomcat.enableNaming();
TomcatEmbeddedServletContainer container =
super.getTomcatEmbeddedServletContainer(tomcat);
for (Container child: container.getTomcat().getHost().findChildren()) {
if (child instanceof Context) {
ClassLoader contextClassLoader =
((Context)child).getLoader().getClassLoader();
Thread.currentThread().setContextClassLoader(contextClassLoader);
break;
}
}
return container;
}
@Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setName("jdbc/myDataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", "your.db.Driver");
resource.setProperty("url", "jdbc:yourDb");
context.getNamingResources().addResource(resource);
}
};
}
getEmbeddedServletContainer
提取上下文的类加载器,并将其设置为当前线程的上下文类加载器。这是 在 调用super方法之后发生的。这种顺序非常重要,因为对super方法的调用会创建并启动容器,并在创建过程中创建上下文的类加载器。