1. 概述

本教程将深入探讨如何在 Spring 应用中使用 XML 和 Java 配置方式来处理静态资源的访问

2. 使用 Spring Boot

Spring Boot 默认集成了 ResourceHttpRequestHandler 的实现,方便开发者快速提供静态资源服务。

默认情况下,该处理器会从类路径下的 /static、/public、/resources 和 /META-INF/resources 目录中加载静态资源

由于 src/main/resources 通常位于类路径下,我们可以直接将这些目录放在该路径中。

例如,若我们将 about.html 文件放置于类路径中的 /static 目录下,则可以通过 http://localhost:8080/about.html 访问该文件。同样的效果也可以通过将文件放在其他指定目录实现。

2.1. 自定义路径匹配规则

✅ **默认情况下,Spring Boot 会将所有静态资源映射到根路径下,即 /****。

虽然这个默认配置看起来很合理,但我们仍可通过 spring.mvc.static-path-pattern 属性来自定义路径匹配规则。

例如,如果我们希望将资源访问路径改为 http://localhost:8080/content/about.html,可以在 application.properties 中添加如下配置:

spring.mvc.static-path-pattern=/content/**

⚠️ 如果是 WebFlux 环境,请使用 spring.webflux.static-path-pattern 属性。

2.2. 自定义资源目录

和路径匹配类似,我们也可以通过 spring.web.resources.static-locations 属性来自定义静态资源的位置。该属性支持多个以逗号分隔的资源路径:

spring.web.resources.static-locations=classpath:/files/,classpath:/static-files

✅ 上述配置表示从类路径下的 /files/static-files 目录中加载静态资源。

此外,Spring Boot 还支持从类路径之外的位置加载资源:

spring.web.resources.static-locations=file:/opt/files

这里我们使用了 file 资源标识符 *file:/*,用于从本地磁盘加载资源。

3. XML 配置方式

如果项目仍使用传统的 XML 配置方式,可以使用 mvc:resources 元素来指定静态资源的 URL 模式与物理路径。

例如,以下配置会将所有以 /resources/** 为前缀的请求映射到应用根目录下的 /resources/ 文件夹中:

<mvc:resources mapping="/resources/**" location="/resources/" />

此时,我们可以在 HTML 页面中通过如下方式引用 CSS 文件:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
</body>
</html>

4. ResourceHttpRequestHandler 详解

从 Spring 3.1 开始引入了 ResourceHandlerRegistry,用于配置 ResourceHttpRequestHandler,从而从类路径、WAR 包或文件系统中提供静态资源。我们可以在 Web 配置类中通过编程方式配置 ResourceHandlerRegistry

4.1. 提供 WAR 包内的资源

我们继续使用之前的 URL 示例来访问 myCss.css,但这次文件位于 WAR 包的 webapp/resources 目录中,这是部署 Spring 3.1+ 应用时推荐的静态资源存放位置:

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
          .addResourceHandler("/resources/**")
          .addResourceLocations("/resources/");    
    }
}

简单分析一下这段代码:

  • 我们通过 addResourceHandler 定义了对外暴露的 URL 路径;
  • 通过 addResourceLocations 映射到实际资源所在的物理路径。

当然,我们可以通过这套简洁而灵活的 API 来定义多个资源处理器。

此时,HTML 页面中可通过如下方式引用资源:

<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">

4.2. 提供文件系统中的资源

假设我们要将 /opt/files/ 目录中的资源映射到 /files/** 路径上,只需简单配置即可:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/files/**")
      .addResourceLocations("file:/opt/files/");
}

📌 Windows 用户应将路径写为:file:///C:/opt/files/

配置完成后,我们就可以在 home.html 中引用文件系统中的图片资源:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
    <title>Home</title>
</head>
<body>
    <h1>Hello world!</h1>
    <img alt="image"  src="<c:url value="files/myImage.png" />">
</body>
</html>

4.3. 配置多个资源位置

如果希望在多个位置中查找资源,可以通过 addResourceLocations 方法传入多个路径。系统会按顺序查找,直到找到资源为止:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("/resources/","classpath:/other-resources/");
}

例如,通过以下 curl 请求可以访问 webapp/resources 或类路径下的 other-resources 中的 Hello.html 文件:

curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html

5. 新增的 ResourceResolvers

Spring 4.1 引入了新的 ResourceResolvers,可用于优化浏览器加载静态资源的性能。这些解析器可以链式调用并缓存资源,从而提升请求处理效率。

5.1. PathResourceResolver

这是最基础的解析器,用于根据 URL 模式查找资源。如果我们未显式添加任何解析器,默认使用的就是这个。

示例:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("/resources/","/other-resources/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new PathResourceResolver());
}

📌 注意事项:

  • 我们将 PathResourceResolver 添加到资源链中作为唯一解析器;
  • 资源将在浏览器中缓存 3600 秒;
  • 通过 resourceChain(true) 启用资源链。

此时,HTML 中可以通过如下方式引用脚本:

<script type="text/javascript" src="<c:url value="/resources/foo.js" />">

5.2. EncodedResourceResolver

该解析器会根据 Accept-Encoding 请求头来查找压缩版本的资源(如 gzip)。

例如,我们可以通过 gzip 压缩来优化带宽使用:

registry
  .addResourceHandler("/other-files/**")
  .addResourceLocations("file:/Users/Me/")
  .setCachePeriod(3600)
  .resourceChain(true)
  .addResolver(new EncodedResourceResolver());

默认支持 brgzip 编码。

通过以下 curl 请求可获取压缩版本的资源:

curl -H "Accept-Encoding:gzip" http://localhost:8080/handling-spring-static-resources/other-files/Hello.html

📌 注意:

  • 请求头中的 Accept-Encoding 必须为 gzip;
  • 压缩资源会在浏览器中缓存 3600 秒。

5.3. 链式 ResourceResolvers

为了优化资源查找,多个 ResourceResolvers 可以组成链式结构。唯一不能委托给链的是 PathResourceResolver,应将其放在链的末尾。

示例:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/js/**")
      .addResourceLocations("/js/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new GzipResourceResolver())
      .addResolver(new PathResourceResolver());
}

📌 注意:从 Spring Framework 5.1 开始,*GzipResourceResolver* 已被弃用,推荐使用 EncodedResourceResolver

此时,HTML 中引用 JS 文件如下:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet" />
    <script type="text/javascript"  src="<c:url value="/js/foo.js" />"></script>
    <title>Home</title>
</head>
<body>
    <h1>This is Home!</h1>
    <img alt="bunny hop image"  src="<c:url value="files/myImage.png" />" />
    <input type = "button" value="Click to Test Js File" onclick = "testing();" />
</body>
</html>

6. 安全配置补充

如果项目使用了 Spring Security,需确保静态资源路径是可访问的。可通过如下配置开放权限:

<intercept-url pattern="/files/**" access="permitAll" />
<intercept-url pattern="/other-files/**/" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />

7. 总结

本文介绍了 Spring 应用中处理静态资源的多种方式:

  • XML 配置是一种传统方式,适用于无法使用 Java 配置的场景;
  • Spring 3.1 引入的 ResourceHandlerRegistry 提供了基础的程序化配置能力;
  • Spring 4.1 引入的 ResourceResolversResourceChainRegistration 提供了缓存、链式处理等优化功能,显著提升了静态资源的加载效率。

📌 示例代码可在 GitHub 获取。Spring Boot 相关示例请参考 此项目


原始标题:Serve Static Resources with Spring

« 上一篇: Spring 事件机制详解
» 下一篇: Baeldung每周评论45