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());
默认支持 br 和 gzip 编码。
通过以下 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 引入的 ResourceResolvers 和 ResourceChainRegistration 提供了缓存、链式处理等优化功能,显著提升了静态资源的加载效率。