Spring Boot 和 Jersey 产生 ClassNotFound

Spring Boot and Jersey produces ClassNotFound

我正在尝试打包我的 Spring 使用 Jersey 的引导应用程序。在开发过程中启动应用程序时 运行 没有问题,当我使用

生成 jar 文件时出现问题
mvnw package && java -jar target/gs-spring-boot-docker-0.1.0.jar

这会产生以下错误。

Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied depend ency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jerseyConfig' defined in URL [jar:file:..path/Backend/target/celulascontentas-2.0.jar!/BOOT-INF/classes!/tech/aabo/celulascontenta s/config/JerseyConfig.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [tech.aabo.ce lulascontentas.config.JerseyConfig]: Constructor threw exception; nested exception is org.glassfish.jersey.server.internal.scanning.ResourceFinderException: java.io.File NotFoundException: ..path\Backend\target\celulascontentas-2.0.jar!\BOOT-INF\classes (The system cannot find the path s pecified)

我的 Jersey 配置如下所示:

package tech.aabo.celulascontentas.config;

import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.springframework.stereotype.Component;

import javax.ws.rs.ApplicationPath;

@Component
@ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(JacksonJaxbJsonProvider.class);
        packages("tech.aabo.celulascontentas.endpoint");
        property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
    }
}

我的 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>

    <groupId>tech.aabo</groupId>
    <artifactId>celulascontentas</artifactId>
    <version>2.0</version>
    <packaging>jar</packaging>

    <name>celulascontentas</name>
    <description>API para celulas contentas</description>

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

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- Core -->

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

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

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

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

        <!-- DB -->

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Extra -->

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk</artifactId>
            <version>1.11.133</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/target/</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>src/main/resources</directory>
                                    <filtering>true</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

我试过各种组合,但总是产生相同的错误。那些人现在如何解决它?

谢谢

Jersey classpath scanning limitations

The change to the layout of executable jars means that a limitation in Jersey’s classpath scanning now affects executable jar files as well as executable war files. To work around the problem, classes that you wish to be scanned by Jersey should be packaged in a jar and included as a dependency in BOOT-INF/lib. The Spring Boot launcher should then be configured to unpack those jars on start up so that Jersey can scan their contents.

这是一个已知问题,有一个公开的 pull request that's not merged yet. I suggest you refer this issue 提到了一些解决方法。

解决方法很少

  1. Configure Boot's Maven plugin to unpack the jars containing that package

  2. Use the below workaround

jersey 1:

import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ws.rs.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.sun.jersey.api.core.DefaultResourceConfig;

@Component
public class RestApplication extends DefaultResourceConfig {

    private static final Logger log = LoggerFactory.getLogger(RestApplication.class);

    public RestApplication() {
        getFeatures().put("com.sun.jersey.api.json.POJOMappingFeature", true);
    }

    @Autowired
    ApplicationContext appCtx;

    @PostConstruct
    public void setup() {
        log.info("Rest classes found:");
        Map<String,Object> beans = appCtx.getBeansWithAnnotation(Path.class);
        for (Object o : beans.values()) {
            log.info(" -> " + o.getClass().getName());
            getSingletons().add(o);
        }
    }

}
and

    @Autowired
    RestApplication restApplication;

    @Bean
    public ServletRegistrationBean jersey() {
        ServletRegistrationBean bean = new ServletRegistrationBean();
        bean.setServlet(new ServletContainer(restApplication));
        bean.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
        bean.setUrlMappings(Arrays.asList("/rest/*"));
        return bean;
    }
jersery2:

import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Path;

import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.sun.jersey.api.core.ResourceConfig;

@Component
@ApplicationPath("/rest")
public class JerseyConfig extends ResourceConfig {

    private static final Logger log = LoggerFactory.getLogger(JerseyConfig.class);

    @Autowired
    ApplicationContext appCtx;

    @PostConstruct
    public void setup() {
        log.info("Rest classes found:");
        Map<String,Object> beans = appCtx.getBeansWithAnnotation(Path.class);
        for (Object o : beans.values()) {
            log.info(" -> " + o.getClass().getName());
            register(o);
        }
    }

}