HTTP 缓存 (HTTP Caching)
HTTP 缓存可以显著提高 Web 应用的性能。它主要通过 Cache-Control 响应头以及随后的条件请求(如 ETag 和 Last-Modified)来实现。
核心技术
1. CacheControl
Spring 提供了 CacheControl 类来简化 Cache-Control 标头的配置。
java
// 缓存一小时
CacheControl cc = CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic();
// 禁止缓存
CacheControl noCache = CacheControl.noStore();kotlin
val cc = CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()
val noCache = CacheControl.noStore()2. 控制器配置
你可以直接在 ResponseEntity 中设置缓存规则。
java
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
Book book = findBook(id);
String etag = book.getVersion();
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
.eTag(etag) // 也可以使用 lastModified
.body(book);
}3. Shallow ETag 过滤器
如果你不想在业务代码中手动管理 ETag,可以注册 ShallowEtagHeaderFilter。它会根据响应内容自动计算 MD5 哈希值。虽然它节省了网络带宽(如果内容没变会返回 304),但并不能节省服务器计算 HTML/JSON 的 CPU 时间。
补充教学
1. 强缓存 vs 协商缓存
- 强缓存 (
max-age):浏览器在过期前直接使用本地备份,不发请求。 - 协商缓存 (
ETag/Last-Modified):浏览器询问服务器“我手里的东西变了吗?”。如果没变(304 Not Modified),服务器返回空包体,节省流量。
2. ETag 的精妙之处
相比于 Last-Modified(只能精确到秒,且文件内容不变但修改时间变了也会失效),ETag 是内容的指纹。 Spring 的做法:Spring 系统地支持了规范中的条件分支。如果是 GET/HEAD 请求且匹配,返回 304;如果是 PUT/DELETE 请求且不匹配,返回 412 (Precondition Failed),有效防止“丢失更新”问题。
3. 静态资源的缓存优化
在 Spring MVC 的资源配置中,务必为静态文件设置长效缓存。配合文件名加哈希值(版本化资源链接)的策略,可以实现“永不过期”的效果,直到你发布新版本。