【问题标题】:Can't serve static content with Spring Boot and Jersey 2无法使用 Spring Boot 和 Jersey 2 提供静态内容
【发布时间】:2026-02-06 05:10:01
【问题描述】:

有没有办法让带有 Jersey 的 Spring Boot 提供静态内容?我已经阅读了一系列关于将 Swagger 集成到 Spring Boot 应用程序中的教程和代码示例。我可以让它提供基本的 swagger.json,但我不能让 Swagger UI 工作。

我什至不能让它提供一个简单的hello.txt 静态文件。

我的 pom.xml 的相关部分是:

<!--Spring Boot-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Jersey -->
<dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>${jersey.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring3</artifactId>
    <version>${jersey.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-bean-validation</artifactId>
    <version>${jersey.version}</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>${jersey.version}</version>
</dependency>

<!-- Swagger -->
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-jersey2-jaxrs</artifactId>
    <version>1.5.7</version>
</dependency>

还有我的代码:

@Configuration
@EnableAutoConfiguration
@ComponentScan({"com.xxxx"})
public class AdminApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(AdminApplication.class)
                .run(args);
    }

    @Bean
    public ServletRegistrationBean jerseyServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
        registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
        return registration;
    }
}




package com.xxxxxx.admin.config;
import com.xxxxxx.admin.resource.Status;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;    
import io.swagger.jaxrs.config.BeanConfig;

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(RequestContextFilter.class);
        packages("com"); // TODO needs more detailed level
        register(LoggingFilter.class);
        // Validation
        this.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
        this.property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
        configureSwagger();
    }

    private void configureSwagger() {
        register(io.swagger.jaxrs.listing.ApiListingResource.class);
        register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("1.0.0");
        beanConfig.setSchemes(new String[]{"http"});
        beanConfig.setHost("localhost:8080");
        beanConfig.setBasePath("/"); // tried other things like "/api", but doesn't change anything
        beanConfig.setResourcePackage("com.xxxxxx.admin");
        beanConfig.setPrettyPrint(true);
        beanConfig.setScan(true);
    }

}



//other imports
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@Service
@Path("/status")
@Api(value = "status", description = "Check status")
public class Status {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation("Return status")
    public Response status() {
        return Response.ok("Up").build();
    }
}

我还尝试让 Jersey 作为过滤器运行(使用 spring.jersey.type=filter)并更改 Jersey 的 servlet 模式,如 this answer 中所述,但这似乎没有任何影响。

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

我在 /src/main/resources/public 下有一个 hello.txt 文件,在 /src/main/resources/public/swagger 下有一个 Swagger UI 的静态文件。

正如我所说,我的应用程序运行良好,GET http://localhost:8080/swagger.json 向我显示了普通的 json 文档,但 http://localhost:8080/hello.txthttp://localhost:8080/swagger/index.html 都返回 404。

我正在使用 Jersey 2.8 和 Spring Boot 1.3.0

【问题讨论】:

    标签: java spring-boot swagger jersey-2.0 swagger-ui


    【解决方案1】:

    我也尝试过改变 Jersey 的 servlet 模式

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

    您配置应用的方式,@ApplicationPath 无关紧要。它适用于 this answer you linked to 的原因是,当 Spring Boot 自动配置从您的资源配置中提取 @ApplicationPath 值时,它会设置 servlet 映射。

    您当前没有使用 Spring Boot 提供的 ServletRegistrationBean 来完成此操作。如果您的目标是使用您自己的ServletRegistrationBean,以便您可以注册您的ResourceConfig,那么您可以通过以下任一方式完成同样的操作

    1. 使用 @Component 注释您的 ResourceConfig 以使其成为 Spring bean,或者
    2. 在你的配置类中让它成为一个 Spring bean

      @Bean
      public ResourceConfig config() {
          return new JerseyConfig();
      }
      

    然后,Spring Boot 会将您的 ResourceConfig 注入到 JerseyAutoConfiguration 中,在那里它将获取 ResourceConfig 上的 @ApplicationPath 值(如果存在),并使用它来注册自己的 ServletRegistrationBean

    当您让 Spring Boot 处理配置时,您可以查看 JerseyAutoConfiguration 以了解您免费获得的一切。

    如果您想保留当前的SpringRegistrationBean,只需更改您正在使用的路径即可。您正在使用/*,这是链接答案中提到的问题。因此,如果您愿意,只需更改为 /rooPath/*

    【讨论】:

    • 这是一个很好的答案,谢谢!但是有一些奇怪的东西。我设置了spring.jersey.type=filterspring.jersey.application-path=rootPath,一切正常:提供静态内容,应用程序端点移动到rootPath/ 并正确回复。但是如果我只设置spring.jersey.type=filter 而不是spring.jersey.application-path,则静态内容将无法访问。根据链接答案中的选项 1,将球衣设置为作为过滤器运行就足够了。我错过了什么?
    • 因为还需要将属性ServletProperties.FILTER_FORWARD_ON_404设置为true。见this answer
    • 我忘了说你是对的,我添加了我自己的ServletRegistrationBean 来注册ResourceConfig。我听从了您的建议,并用@Component 注释了ResourceConfig,并且不再使用我自己的ServletRegistrationBean
    • 好吧,也就是说我不可能把静态内容和球衣资源放在同一个路径下,对吧?
    • 不可以,但是当它找不到您的静态内容时,您需要告诉 Jersey(过滤器)转发请求。这就是上述属性的作用。但是对于 servlet,不,你不能。它需要是一个过滤器
    【解决方案2】:

    它看起来和使用 Spring MVC 时的常见问题相同。每个 servlet 规范都需要一个 servlet 容器来实现具有最低优先级的默认服务器,该服务器能够提供位于 WEB-INF 文件夹之外的静态内容。 不幸的是,您将 Jersey servlet 映射到 "/*",这意味着每个 URL 都将提交给 Jersey,而 Jersey 不知道如何处理静态 URL。

    那么可以(轻松)完成什么?

    • 将 Jersey servlet 映射到子路径(例如 /api)并将所有控制器移到那里:

      ServletRegistrationBean registration =
          new ServletRegistrationBean(new ServletContainer(), "/api/*");
      ...
      beanConfig.setBasePath("/api/");
      

      然后问GET http://localhost:8080/api/swagger.json

    • 仅将 servlet 映射到 *.json URL:

      ServletRegistrationBean registration =
          new ServletRegistrationBean(new ServletContainer(), "*.json");
      

    【讨论】: