SpringBoot JNDI数据源抛出java.lang.ClassNotFoundException:org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory

时间:2016-09-02 05:33:12

标签: tomcat spring-boot spring-data spring-batch

之前已经提出了类似的问题,我经历了所有这些问题但却无法解决问题。相关问题 - Q1Q2Q3Q4Q5Q6

我有一个Spring Batch项目,使用Spring Boot并尝试使用数据库连接池。我使用的是版本为8.5.x的嵌入式tomcat容器。

如果我使用application.properties指定数据源和池设置,一切正常。

但是当我尝试使用JNDI时,我得到了异常 -

Caused by: java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory

我在Maven jar中看不到任何jar名tomcat-dbcp-**,因此我不确定是否需要包含任何新的依赖项或需要设置默认数据源工厂以及如何进行操作。

以下是我设置的JNDI bean Question。我已经消除了某些价值观。

@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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <packaging>war</packaging>

    <groupId>***</groupId>
    <artifactId>***</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.2.1.RELEASE</version>
        </dependency>   

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>db2</groupId>
            <artifactId>db2jcc</artifactId>
            <version>4.0</version>
        </dependency>

        <dependency>
            <groupId>db2</groupId>
            <artifactId>db2jcc_license_cu</artifactId>
            <version>4.0</version>
        </dependency>
    </dependencies>

    <build>

        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.4.0.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                <source>1.7</source>
                <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3 个答案:

答案 0 :(得分:11)

我通过在factory定义中设置Resource属性来解决问题。 resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");

@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("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);
        }
    };
}

根据tomcat 8文档,它应该通过查看DataSource类型自动推断数据库池工厂类型,并且它以某种方式默认为DBCP工厂,并且该类在我的类路径中不存在。

我想这个问题可以通过使tomcat-dbcp-**罐可用来解决,但我不知道如何使用spring boot或者即使弹簧启动可以实现。

我发现奇怪的是Spring Boot不包括tomcat-dbcp依赖项作为启动POM的一部分,但使用DBCP DataSource工厂作为默认工厂。

答案 1 :(得分:3)

如果你的话,“Starter POM”不再包含jndi reltead依赖项 正在使用Tomcat / Jetty / etc ...使用JNDI,您现在需要自己直接添加此依赖项。

然后在application.properties文件中配置JNDI     spring.datasource.jndi-name=java:comp/env/jdbc/yourname

对于您的例外,您需要将tomcat-dbcp添加到您的pom.xml文件中。

但是如果使用spring-boot-starter-jdbc或spring-boot-starter-data-jpa'starters',你可以查看你的项目依赖项 将自动获得对&#34; tomcat-jdbc&#34;。

的依赖

答案 2 :(得分:1)

您有多种选择:

  • 使用默认的DBCP 2数据源(您不希望使用过时且效率较低的DBCP 1)。
  • 使用Tomcat JDBC数据源。
  • 使用任何其他数据源:例如HikariCP。

1)要使用Apache JDBC数据源,不需要添加任何依赖项,因为它已在Tomcat Spring Boot启动程序中提供,但是您必须将默认工厂类更改为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>

当然,请根据您的Spring Boot Tomcat嵌入式版本调整构件版本。

3)要使用任何其他数据源,我将用HikariCP进行说明,如果配置中不存在所需的依赖项,则添加它(如果您依赖Spring Boot的持久性启动程序,则可能是HikariCP):

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.1.0</version>
</dependency>

并在资源声明中指定随附的工厂:

resource.setProperty("factory", "com.zaxxer.hikari.HikariDataSource");

例如对于PostgreSQL和DBCP 2数据源,请不要指定任何工厂,因为它是默认工厂:

    @Override 
    protected void postProcessContext(Context context) {            
        ContextResource resource = new ContextResource();
        //...
        context.getNamingResources()
               .addResource(resource);          
    }

这是Tomcat JDBC和HikariCP数据源的变体。

postProcessContext()中,按照前面针对Tomcat JDBC ds的说明设置工厂属性:

    @Override 
    protected void postProcessContext(Context context) {
        ContextResource resource = new ContextResource();       
        //...
        resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
        //...
        context.getNamingResources()
               .addResource(resource);          
    }
};

对于HikariCP:

    @Override 
    protected void postProcessContext(Context context) {
        ContextResource resource = new ContextResource();       
        //...
        resource.setProperty("factory", "com.zaxxer.hikari.HikariDataSource");
        //...
        context.getNamingResources()
               .addResource(resource);          
    }
};