SpringBoot JNDI数据源抛出java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
SpringBoot JNDI数据源抛出java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
之前有类似的问题被问过,我查看了所有的问题,但是无法解决问题。相关问题 - Q1,Q2,Q3,Q4,Q5,Q6
我有一个使用Spring Boot的Spring Batch项目,尝试使用DB连接池。我正在使用版本为8.5.x的嵌入式tomcat容器。
如果我使用application.properties来指定数据源和池设置,一切都正常工作。
但是当我尝试使用JNDI时,我会遇到异常 -
Caused by: java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
在Maven jars中,我没有看到任何jar名称为tomcat-dbcp-**
,所以我不确定是否需要包含任何新的依赖项或需要设置默认数据源工厂以及如何进行设置。
下面是我的JNDI bean设置,问题。我已经清空了某些值。
@Bean public TomcatEmbeddedServletContainerFactory embeddedServletContainerFactory(){ return new TomcatEmbeddedServletContainerFactory() { @Override protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatEmbeddedServletContainer(tomcat); } @Override protected void postProcessContext(Context context) { ContextResource resource = new ContextResource(); resource.setName("jdbc/myDataSource"); resource.setType(DataSource.class.getName()); resource.setProperty("driverClassName", "com.ibm.db2.jcc.DB2Driver"); resource.setProperty("url", "url"); resource.setProperty("username", "user"); resource.setProperty("password", "*****"); context.getNamingResources().addResource(resource); } }; } @Lazy @Bean(destroyMethod="") public DataSource jndiDataSource() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean bean = new JndiObjectFactoryBean(); bean.setJndiName("java:comp/env/jdbc/myDataSource"); bean.setProxyInterface(DataSource.class); bean.setLookupOnStartup(false); bean.afterPropertiesSet(); return (DataSource)bean.getObject(); }
我的pom.xml
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
出现这个问题的原因是因为在Spring Boot中使用JNDI数据源时,没有正确配置数据源的工厂类。可以通过以下几种方式解决这个问题:
1)使用Apache JDBC数据源,不需要添加任何依赖项,因为它已经在Tomcat Spring Boot starter中提供了。但是需要将默认的工厂类更改为org.apache.tomcat.jdbc.pool.DataSourceFactory
。可以在资源声明中添加以下代码来实现:
resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
2)使用DBCP 2数据源(默认情况下),需要添加以下依赖项:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>8.5.4</version>
</dependency>
3)使用其他数据源,例如HikariCP,如果尚未在配置中添加所需的依赖项,则需要添加。同时在资源声明中指定相应的工厂类,例如:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.1.0</version> </dependency>
resource.setProperty("factory", "com.zaxxer.hikari.HikariDataSource");
对于Tomcat JDBC和HikariCP数据源的变体,可以在postProcessContext()
方法中设置工厂属性。例如,对于Tomcat JDBC数据源:
protected void postProcessContext(Context context) { ContextResource resource = new ContextResource(); //... resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory"); //... context.getNamingResources() .addResource(resource); } };
对于HikariCP数据源:
protected void postProcessContext(Context context) { ContextResource resource = new ContextResource(); //... resource.setProperty("factory", "com.zaxxer.hikari.HikariDataSource"); //... context.getNamingResources() .addResource(resource); } };
问题的出现原因:
根据Tomcat 8的文档,它应该通过检查DataSource
类型来自动推断数据库连接池的工厂类型,但它默认使用DBCP工厂,而在我的类路径中找不到该类。在Spring Boot中,这个问题可能可以通过使tomcat-dbcp-**
的jar包可用来解决,但我不确定如何在Spring Boot中实现,甚至不确定是否可以在Spring Boot中实现。我觉得奇怪的是,Spring Boot在starter POM的一部分中没有包含tomcat-dbcp依赖,却将DBCP DataSource工厂作为默认工厂。
解决方法:
我通过在Resource
定义中设置factory
属性来解决了这个问题。resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
此外,我还配置了TomcatEmbeddedServletContainerFactory并重写了其中的两个方法。其中一个方法是启用了Tomcat的命名,另一个方法是在Tomcat的上下文中添加了一个ContextResource资源,并设置了相关的属性(如驱动类名、URL、用户名、密码等)。
代码如下:
public TomcatEmbeddedServletContainerFactory embeddedServletContainerFactory(){ return new TomcatEmbeddedServletContainerFactory() { protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer( Tomcat tomcat) { tomcat.enableNaming(); return super.getTomcatEmbeddedServletContainer(tomcat); } protected void postProcessContext(Context context) { ContextResource resource = new ContextResource(); resource.setName("jdbc/myDataSource"); resource.setType(DataSource.class.getName()); resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory"); resource.setProperty("driverClassName", "com.ibm.db2.jcc.DB2Driver"); resource.setProperty("url", "url"); resource.setProperty("username", "user"); resource.setProperty("password", "*****"); context.getNamingResources().addResource(resource); } }; }
问题出现的原因是Spring Boot的Starter POM不再包含与JNDI相关的依赖项。如果你正在使用Tomcat/Jetty等容器与JNDI一起使用,现在你需要自己直接添加这个依赖项。解决方法是在pom.xml文件中添加tomcat-dbcp依赖。
同时,你还需要在应用的application.properties文件中配置JNDI:
spring.datasource.jndi-name=java:comp/env/jdbc/yourname
如果你使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa "starters",你将自动获取对"tomcat-jdbc"的依赖。但是你可以检查你的项目依赖,确认是否需要手动添加tomcat-dbcp依赖。
关于"jndi reltead dependencies"的具体内容以及是否在bean定义中设置了相同的属性,文章中没有提供更多信息。