开发者

Spring HTTP缓存应用场景举例

目录
  • 一、什么是 HTTP 缓存?
    • 核心原理:Cache-Control+ 条件请求(ETag / Last-Modified)
  • 二、CacheControl类:简化 Cache-Control 配置
    • 示例代码解析:GGwNl
  •  三、在控制器中使用 HTTP 缓存(Controllers)
    • ✅ 推荐做法:返回ResponseEntity并添加 ETag 和 Cache-Control
      • 发生了什么?
    • ⚙️ 手动检查条件请求(更灵活控制)
      • checkNotModified()做了什么?
  • 四、静态资源缓存(Static Resources)
    • 五、Shallow ETag Filter(浅层 ETag 过滤器)
      • 问题引入:
        • 特点:
          • 使用方式(web.XML 或 Java Config 添加过滤器):
          • ✅ 总结:如何理解并运用 HTTP 缓存?
            • 实际应用场景举例
              • 场景1:图书详情页 API
                • 场景2:首页 html(不希望缓存)
                •  学习建议

                  以下内容是来自 Spring Framework 官方文档 中关于 HTTP 缓存(HTTP Caching) 的部分,特别是针对 WebFlux 和 Web MVC 框架的说明。下面我将用通俗易懂的方式为你逐段解析,并帮助你理解其核心概念和实际应用。

                  一、什么是 HTTP 缓存?

                  原文开头:

                  HTTP caching can significantly improve the performance of a web application.

                  HTTP 缓存是一种优化机制,它的目标是:

                  让浏览器或中间代理服务器(如 CDN)避免重复下载相同的资源,从而:

                  • 减少网络请求
                  • 节省带宽
                  • 提升页面加载速度
                  • 降低后端服务器压力

                  核心原理:Cache-Control+ 条件请求(ETag / Last-Modified)

                  • Cache-Control
                    • 控制资源可以被谁缓存、缓存多久。
                    • 例如:Cache-Control: max-age=3600 表示“这个响应可以缓存 1 小时”。
                  • 条件请求头(Conditional Request Headers)
                    • 当缓存过期或需要验证是否更新时,浏览器会带上:
                    • 如果内容没变,服务器就返回 304 Not Modified(不传正文),节省传输开销。

                  If-None-Match: 对应服务器之前返回的 ETag

                  If-Modified-Since: 对应之前的 Last-Modified

                  二、CacheControl类:简化 Cache-Control 配置

                  Spring 提供了一个工具类 CacheControl,让你不用手动拼字符串设置 Cache-Control 头。

                  示例代码解析:

                  // 缓存1小时:"Cache-Control: max-age=3600"
                  CacheControl.maxAge(1, TimeUnit.HOURS);
                  // 禁止缓存:"Cache-Control: no-store"
                  CacheControl.noStore();
                  // 自定义策略
                  CacheControl.maxAge(10, TimeUnit.DAYS)
                              .noTransform()
                              .cachePublic();
                  // 结果:"max-age=864000, public, no-transform"
                  方法含义
                  maxAge(...)设置最大缓存时间(秒数)
                  noStore()禁止任何缓存(敏感数据常用)
                  cachePublic()允许公共缓存(如 CDN)缓存
                  cachePrivate()只允许私有缓存(如浏览器)
                  noTransform()防止中间代理压缩或转换内容

                  这些方法链式调用,语义清晰,比直接写 header 更安全、易读。

                   三、在控制器中使用 HTTP 缓存(Controllers)

                  这是最常见也最重要的场景 —— 给动态接口加上缓存支持。

                  ✅ 推荐做法:返回ResponseEntity并添加 ETag 和 Cache-Control

                  @GetMapping("/book/{id}")
                  public ResponseEntity<Book> showBook(@PathVariable Long id) {
                      Book book = findBook(id);
                      String version = book.getVersion(); // 如数据库版本号、hash值等
                      return ResponseEntity
                          .ok()
                          .cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS)) // 缓存30天
                          .eTag(version)  // 设置ETag
                          .body(book);
                  }

                  发生了什么?

                  • 第一次请求:
                  • 返回状态码 200 OK
                  • 响应头包含:
                  ETag: "abc123"
                  Cache-Control: max-age=2592000
                  
                  • 浏览器下次请求同一 URL:
                  • 自动带上:
                  If-None-Match: "abc123"
                  
                  • Spring 检查发现 ETag 相同 → 返回 304 Not Modified(空 body)

                  浏览器直接使用本地缓存!

                  ✅ 效果:节省流量,提升用户体验。

                  ⚙️ 手动检查条件请求(更灵活控制)

                  有时候你想先判断是否需要处理业务逻辑:

                  @RequestMapping
                  public String myHandleMethod(WebRequest request, Model model) {
                      long eTag = bookService.getCurrentVersion(); // 应用级计算ETag
                      if (request.checkNotModified(eTag)) {
                          return null; // 不再执行后续逻辑,自动返回304
                      }
                      model.addAttribute("data", ...);
                      return "myViewName";
                  }

                  checkNotModified()做了什么?

                  • 比较客户端发来的 If-None-MatchIf-Modified-Since 是否匹配。
                  • 匹配 → 设置响应为 304 Not Modified,并终止处理 → 返回 null
                  • 不匹配 → 正常继续处理请求

                  注意:

                  • 对于 GET/HEAD 请求,返回 304
                  • 对于 POST/PUT/DELETE 请求,若条件不满足,应返回 412 Precondition Failed(防止并发修改)

                  四、静态资源缓存(Static Resources)

                  比如 js、css、图片等文件,非常适合长期缓存。

                  建议配置:

                  • 设置长时间的 Cache-Control: max-age=31536000(一年)
                  • 使用内容指纹命名(如 app.a1b2c3.js),确保更新后 URL 改变,打破缓存

                  这样既能充分利用缓存,又能保证更新生效。

                  文档提示参考 “Configuring Static Resources” 进行详细配置(通常通过 Spring Boot 的 spring.web.resources.cache 配置项实现)

                  五、Shallow EandroidTag Filter(浅层 ETwww.devze.comag 过滤器)

                  问题引入:

                  上面我们手动设置了 ETag,但如果你不想自己管理 ETag 怎么办?

                  Spring 提供了一种自动方案:ShallowEtagHeaderFilter

                  特点:

                  项目Shallow ETag手动 ETag
                  计算方式基于响应体内容生成 hash开发者根据业务决定(如版本号)
                  是否节省 CPU❌ 仍需完整生成响应内容✅ 可提前中断(未改就不生成)
                  是否节省带宽✅ 若相同则返回 304
                  性能影响高负载下可能增加 CPU 使用率更高效(可短路)

                  使用方式(web.xml 或 Java Config 添加过滤器):

                  <filter>
                      <filter-name>etagFilter</filter-name>
                      <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
                  </filter>
                  <filter-mapping>
                      <filter-name>etagFilter</filter-name>
                      <url-pattern>/*</url-pattern>
                  </filter-mapping>

                  ⚠️ 注意:它只对 GET 请求有效,且必须等到整个响应生成完才计算 ETag,所以不能减少服务器计算量,只能减少传输量。

                  ✅ 总结:如何理解并运用 HTTP 缓存?

                  层面建议做法
                  通用原则利用 Cache-Control 控制缓存策略,用 ETag/Last-Modified 实现条件请求
                  动态接口在 Controller 返回 ResponseEntity,设置 .eTag().cacheControl()
                  静态资源设置长期 max-age,配合文件名哈希
                  不想手动js管ETag?ShallowEtagHeaderFilter 自动生成(牺牲CPU换带宽)
                  想极致性能?手动计算 ETag 并调用 request.checkNotModified() 提前退出

                  实际应用场景举例

                  场景1:图书详情页 API

                  @GetMapping("/api/books/{id}")
                  public ResponseEntity<Book> getBook(@PathVariable Long id, WebRequest request) {
                      Book book = bookService.findById(id);
                      Stringjavascript etag = book.getUpdatedAt().toString(); // 或 MD5(content)
                      if (request.checkNotModified(etag)) {
                          return null;
                      }
                      return ResponseEntity.ok()
                          .cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS))
                          .eTag(etag)
                          .body(book);
                  }

                  效果:

                  • 内容未变 → 返回 304,极快响应
                  • 内容变了 → 返回 200 + 新数据

                  场景2:首页 HTML(不希望缓存)

                  @GetMapping("/")
                  public String home(Model model, WebRequest request) {
                      String etag = System.currentTimeMillis() / 3600 + ""; // 每小时变化
                      if (request.checkNotModified(etag)) {
                          return null;
                      }
                      model.addAttribute("time", new Date());
                      return "home";
                  }

                  搭配 Cache-Control: max-age=3600,实现每小时最多重新生成一次。

                   学习建议

                  • 动手实验:用 Postman 或浏览器开发者工具观察请求头的变化。
                  • 查看 Network Tab:注意 200 vs 304 的区别,以及请求/响应头中的 ETag, If-None-Match, Cache-Control
                  • 结合 Spring Boot:使用 application.yml 配置静态资源缓存:
                  spring:
                    web:
                      resources:
                        cache:
                          cachecontrol:
                            max-age: 1h

                  如有具体需求(比如“我想让某个接口缓存一天”,或“为什么我的 ETag 没生效?”),欢迎继续提问!

                  到此这篇关于Spring HTTP缓存全解析的文章就介绍到这了,更多相关Spring HTTP缓存内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

                  0

                  上一篇:

                  下一篇:

                  精彩评论

                  暂无评论...
                  验证码 换一张
                  取 消

                  最新开发

                  开发排行榜