1. 概述
本教程將探討如何使用 Spring 同時使用 XML 和 Java 配置,提供靜態資源。
2. 使用 Spring Boot
Spring Boot 附帶了預配置的 ResourceHttpRequestHandler 實現,以方便提供靜態資源。
默認情況下,該處理器會從 classpath 中的任何 /static, /public, /resources, 和 /META-INF/resources 目錄提供靜態內容。由於 src/main/resources 通常在 classpath 中,因此我們可以將這些目錄放在其中。
例如,如果我們把一個 about.html 文件放在 classpath 中的 /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我們這裏從 classpath 內部的 /files 和 /static-files 目錄中提供靜態內容。 此外,Spring Boot 還可以從 classpath 之外提供靜態文件:
spring.web.resources.static-locations=file:/opt/files
在這裏,我們使用了 文件資源簽名,file:/,從本地磁盤提供文件。
3. XML 配置
如果需要採用傳統的基於 XML 的配置方式,我們可以利用 <em/>mvc:resources</em/> 元素來指向具有特定公共 URL 模式的資源的存儲位置。
例如,以下行將搜索所有具有公共 URL 模式(例如“/resources/**”),並從應用程序根文件夾下的“/resources/</em/>”目錄中查找資源的請求:
<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. The ResourceHttpRequestHandler
Spring 3.1 introduced the ResourceHandlerRegistry to configure ResourceHttpRequestHandlers for serving static resources from the classpath, the WAR, or the file system.
4.1. 部署 WAR 包中的資源
為了説明這一點,我們將使用之前相同的 URL 指向 <em >myCss.css</em>,但實際的文件將位於 WAR 包的 <em >webapp/resources</em> 目錄下,這是在部署 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 頁面中,以下這一行將獲取 myCss.css 資源,該資源位於 webapp/resources 目錄下:
<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">4.2. 服務文件系統存儲的資源
假設我們希望在接收到針對公共 URL 匹配 /files/** 模式的請求時,服務位於文件系統中的資源,例如位於 /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 目錄或 classpath 中 other-resources 目錄中的 Hello.html 頁面:
curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html5. 新的 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 註冊為資源鏈中的唯一 ResourceResolver。請參閲第 4.3 節,瞭解如何將多個 ResourceResolver 鏈接在一起。
- 提供的資源將會在瀏覽器中緩存 3600 秒。
- 最後,資源鏈已使用方法 resourceChain(true) 進行配置。
現在是用於與 PathResourceResolver 一起使用的 HTML 代碼,它可以在 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 請求將獲取 Home.html 文件壓縮後的版本,該文件位於文件系統中 Users/Me/ 目錄中:
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 來提供資源。 這裏我們通過將 PathResourceResolver 串聯起來,以解決資源,如果 GzipResourceResolver 失敗:
@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 頁面中包含 foo.js 資源,該資源位於 webapp/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 Framework 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 對象,提供資源加載優化功能,例如緩存和資源處理鏈,以提高提供靜態資源的效率。