十、HTTP 缓存——HTTP 缓存与HttpClient客户端缓存
1. HTTP 缓存简介
在日常开发中,浏览器缓存是一个非常常见但又容易被忽视的优化手段。许多开发者在面对浏览器缓存相关问题时可能会感到困惑,因此本章内容专门用来解答这些常见问题,并帮助你更好地理解如何在 Java 应用中控制浏览器缓存。
浏览器缓存是指在用户使用浏览器访问网站页面或 HTTP 服务时,根据服务器端返回的缓存控制响应头,将响应内容缓存到本地浏览器。这样,下次访问相同资源时,浏览器可以直接使用缓存内容,或者仅与服务器验证内容是否过期,从而减少网络传输的数据量,提升网站性能。

1.1 浏览器缓存示例
以 Chrome 浏览器为例,假设我们第一次访问 http://item.jd.com/1856588.html
,将得到如下的响应头:
General
Request URL: http://item.jd.com/1856588.html
Request Method: GET
Status Code: 200 OK
Remote Address: 111.206.231.1:80
Response Headers
Age: 3
Cache-Control: max-age=60
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=gbk
Date: Tue, 16 Aug 2024 12:07:44 GMT
Expires: Tue, 16 Aug 2024 12:08:23 GMT
Last-Modified: Tue, 16 Aug 2024 12:07:25 GMT
Server: jdws
Transfer-Encoding: chunked
Vary: Accept-Encoding
Via: BJ-Y-NX-111(HIT), http/1.1 BJ-UWI-1-3CS-116
这些响应头中,Cache-Control
、Expires
和 Last-Modified
是与缓存机制相关的关键字段。
接下来,如果我们按下 F5
刷新页面,将得到如下响应头:
General
Request URL: http://item.jd.com/1856588.html
Request Method: GET
Status Code: 304 Not Modified
Remote Address: 111.206.231.1:80
Response Headers
Cache-Control: max-age=60
Connection: keep-alive
Date: Tue, 16 Aug 2024 12:07:53 GMT
Expires: Tue, 16 Aug 2024 12:08:23 GMT
Server: jdws
Vary: Accept-Encoding
Via: http/1.1 負I-l-JCS-116
第二次请求返回的状态码是 304 Not Modified
,表示服务器端的文档没有被修改,浏览器可以直接使用缓存的内容。
1.2 在 Java 应用中控制浏览器缓存
要在 Java 应用中控制浏览器缓存,可以通过设置响应头来实现。
以下是一个简单的示例,展示了如何使用 Spring Boot 控制浏览器缓存:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
@RestController
@RequestMapping("/api")
public class CacheController {
@GetMapping("/cache")
public String getCacheableContent(HttpServletResponse response) {
// 设置 Cache-Control 响应头
response.setHeader("Cache-Control", "max-age=" + Duration.ofMinutes(5).getSeconds());
// 设置 Expires 响应头
response.setHeader("Expires", "Wed, 21 Oct 2024 07:28:00 GMT");
// 设置 Last-Modified 响应头
response.setHeader("Last-Modified", "Wed, 21 Oct 2024 07:00:00 GMT");
return "This content is cacheable for 5 minutes.";
}
}
在这个示例中,我们设置了 Cache-Control
、Expires
和 Last-Modified
响应头,浏览器将根据这些头信息缓存内容,并在后续请求中使用缓存数据。
通过合理设置这些响应头,可以有效控制浏览器缓存的行为,提升系统性能并节省带宽。
2. HTTP 缓存响应标头
2.1 Last-Modified
以下是一个Spring MVC的缓存测试代码,用来演示如何使用Last-Modified
头进行HTTP缓存控制。
package com.yyl.study.controller;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @author yangjian
* @date 2024/8/17 09:17:08
*/
@RestController
@RequestMapping("/api")
public class CacheController {
@RequestMapping("/cache")
public ResponseEntity<String> cache(
@RequestHeader(value = "If-Modified-Since", required = false) Date ifModifiedSince) throws Exception {
DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
// 文档最后修改时间 (去掉毫秒值)
long lastModifiedMillis = getLastModified() / 1000 * 1000;
// 当前系统时间 (去掉毫秒值)
long now = System.currentTimeMillis() / 1000 * 1000;
// 文档可以在浏览器端/proxy上缓存的时间 (单位:秒)
long maxAge = 20;
// 判断内容是否修改了
if (ifModifiedSince != null && ifModifiedSince.getTime() == lastModifiedMillis) {
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("Date", gmtDateFormat.format(new Date(now)));
headers.add("Expires", gmtDateFormat.format(new Date(