1. 概述
本教程将探讨如何使用XML和Java配置通过Spring提供静态资源。
延伸阅读
使用Spring MVC的可缓存静态资源
本文展示了在使用Spring MVC服务时如何缓存静态资源,例如Javascript和CSS文件。
阅读更多→
WebJar简介
将WebJars与Spring结合使用的快速实用指南。
阅读更多→
使用Maven压缩JS和CSS资源
使用Maven压缩Java Web项目中的Javascript和CSS文件的快速指南。
阅读更多→
2. 使用Spring Boot
Spring Boot带有ResourceHttpRequestHandler的预配置实现,以促进提供静态资源。
默认情况下,此处理程序提供来自classpath上的任何/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:/来提供本地磁盘中的文件。
3. XML配置
如果我们需要采用基于XML的配置的老式方式,我们可以充分利用mvc:resources元素来指向具有特定公共URL模式的资源位置。
例如,以下行将通过在我们的应用程序根文件夹下的“/resources/”目录中搜索来处理所有使用公共URL模式(如“/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/");
}
}
让我们稍微分析一下这个例子。首先,我们通过定义资源处理程序来配置面向外部的URI路径。然后我们将该面向外部的URI路径在内部映射到资源实际所在的物理路径。
当然,我们可以使用这个简单而灵活的API定义多个资源处理程序。
现在,html页面中的以下行将获取webapp/resources目录中的myCss.css资源:
<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
4.2 提供存储在文件系统中的资源
假设我们希望在请求与/files/**模式匹配的公共URL时提供存储在/opt/files/目录中的资源。我们只需配置URL模式并将其映射到磁盘上的特定位置:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/files/**")
.addResourceLocations("file:/opt/files/");
}
对于Windows用户,此示例中传递给addResourceLocations的参数为“file:///C:/opt/files/”。
一旦我们配置了资源位置,我们就可以在我们的home.html中使用映射的URL模式来加载存储在文件系统中的图像:
<%@ 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请求将显示存储在应用程序的webappp/resources或类路径中的other-resources文件夹中的Hello.html页面:
curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html
5. 新的资源解析器
Spring 4.1通过新的ResourceResolvers提供不同类型的资源解析器,这些资源解析器可用于在加载静态资源时优化浏览器性能。这些解析器可以链接并缓存在浏览器中以优化请求处理。
5.1 PathResourceResolver
这是最简单的解析器,其目的是在给定公共URL模式的情况下查找资源。事实上,如果我们不向ResourceChainRegistration添加ResourceResolver,这就是默认的解析器。
让我们看一个例子:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/","/other-resources/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
注意事项:
- 我们在资源链中将PathResourceResolver注册为其中唯一的ResourceResolver。我们可以参考4.3节,查看如何链接多个ResourceResolver
- 提供的资源将在浏览器中缓存3600秒
- 该链最终使用方法resourceChain(true)配置
现在,对于HTML代码,它与PathResourceResolver一起在webapp/resources或webapp/other-resources文件夹中找到foo.js脚本:
<script type="text/javascript" src="<c:url value="/resources/foo.js" />">
5.2 EncodedResourceResolver
此解析器尝试根据Accept-Encoding请求标头值查找编码资源。
例如,我们可能需要通过使用gzip内容编码提供静态资源的压缩版本来优化带宽。
要配置EncodedResourceResolver,我们需要在ResourceChain中配置它,就像我们配置PathResourceResolver一样:
registry
.addResourceHandler("/other-files/**")
.addResourceLocations("file:/Users/Me/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new EncodedResourceResolver());
默认情况下,EncodedResourceResolver配置为支持br和gzip编码。
因此,以下curl请求将获取位于文件系统Users/Me/目录中的Home.html文件的压缩版本:
curl -H "Accept-Encoding:gzip"
http://localhost:8080/handling-spring-static-resources/other-files/Hello.html
请注意我们是如何将标头的“Accept-Encoding”值设置为gzip的。这很重要,因为只有当gzip内容对响应有效时,这个特定的解析器才会启动。
最后,请注意,与以前一样,压缩版本将在浏览器中缓存的时间段内保持可用,在本例中为3600秒。
5.3 链接ResourceResolvers
为了优化资源查找,ResourceResolvers可以将资源处理委托给其他解析器。唯一不能委托给链的解析器是PathResourceResolver,我们应该将其添加到链的末尾。
事实上,如果resourceChain没有设置为true,那么默认情况下只会使用PathResourceResolver来提供资源。在这里,如果GzipResourceResolver不成功,我们链接PathResourceResolver来解析资源:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/js/**")
.addResourceLocations("/js/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new GzipResourceResolver())
.addResolver(new PathResourceResolver());
}
现在我们已经将/js/**模式添加到ResourceHandler中,让我们包含位于home.html页面的webapp/js/目录中的foo.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>
值得一提的是,从Spring框架5.1开始,GzipResourceResolver已被弃用,取而代之的是EncodedResourceResolver。因此,我们以后应该避免使用它。
6. 额外的安全配置
如果使用Spring Security,则允许访问静态资源很重要。我们需要添加访问资源URL的相应权限:
<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对象。提供资源加载优化功能,如缓存和资源处理器链接,以提高提供静态资源的效率。
与往常一样,本教程的完整源代码可在GitHub上获得。