Spring应用中HTTP 304 Not Modified状态码的适用场景与优势
HTTP 304 Not Modified 在Spring应用中的实战场景与性能价值
核心逻辑回顾
304 Not Modified 是HTTP缓存体系的关键状态码:客户端请求资源时,携带之前服务端返回的Last-Modified(资源最后修改时间)或ETag(资源唯一标识),服务端比对后确认资源未更新,就返回304,让客户端直接复用本地缓存内容,无需传输完整响应体。
实战场景与Spring实现示例
1. 静态资源缓存(JS/CSS/图片)
Spring Boot默认对classpath:/static/下的静态资源做基础缓存配置,可自定义优化强化304逻辑:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/") .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS) .mustRevalidate()); // 强制客户端过期后验证资源状态 } }
用户二次请求同一静态资源时,会自动携带If-Modified-Since请求头,Spring资源处理器会比对文件最后修改时间,一致则返回304,避免重复传输大体积静态文件。
2. 低频更新的动态内容(文章详情、用户资料)
对于不频繁改动的动态页面/数据,可手动实现304验证逻辑:
@GetMapping("/article/{id}") public ResponseEntity<Article> getArticle(@PathVariable Long id, @RequestHeader(value = "If-Modified-Since", required = false) String ifModifiedSince) { Article article = articleService.getById(id); if (article == null) { return ResponseEntity.notFound().build(); } // 把文章更新时间转为HTTP标准的GMT格式 String lastModified = DateTimeFormatter.RFC_1123_DATE_TIME.format( article.getUpdateTime().atZone(ZoneId.of("GMT"))); if (lastModified.equals(ifModifiedSince)) { // 资源未修改,返回304 return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); } // 返回资源并携带Last-Modified响应头 return ResponseEntity.ok() .header(HttpHeaders.LAST_MODIFIED, lastModified) .body(article); }
用户刷新页面时,若文章未更新,服务端直接返回304,客户端复用本地缓存内容,减少数据库查询和响应体传输开销。
3. 公共数据API接口(城市列表、商品分类)
针对更新频率极低的公共数据接口,用ETag做精确校验(比Last-Modified更适合内容无时间变化但可能被修改的场景):
@GetMapping("/api/cities") public ResponseEntity<List<City>> getCities(@RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) { List<City> cities = cityService.getAllCities(); // 用数据的MD5生成唯一ETag String etag = "\"" + DigestUtils.md5DigestAsHex(JacksonUtils.writeValueAsBytes(cities)) + "\""; if (etag.equals(ifNoneMatch)) { return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); } return ResponseEntity.ok() .header(HttpHeaders.ETAG, etag) .body(cities); }
比如编辑城市名称后又改回原内容,Last-Modified会变化但内容不变,ETag能准确识别资源未改动,返回304。
最佳实践
- 优先复用Spring原生缓存能力:静态资源尽量依赖Spring Boot自动配置,避免手动重复实现基础逻辑;
- 按需选择校验方式:文件类资源用
Last-Modified(实现简单),动态内容优先用ETag(校验更精确); - 合理设置缓存过期策略:静态资源可设30天有效期,动态内容设1-24小时,同时启用
mustRevalidate,确保资源更新后客户端能获取最新版本; - 避免滥用304:实时性要求极高的资源(如实时订单状态)禁用304,防止客户端获取旧数据。
性能与效率提升点
- 大幅减少带宽消耗:304响应体为空,相比传输完整响应体,静态资源场景可节省90%以上带宽;
- 降低服务器负载:减少数据库查询、序列化/反序列化操作,降低CPU和内存占用,提升服务器并发处理能力;
- 优化用户体验:客户端直接复用本地缓存,页面加载速度显著提升,减少等待时间。
内容的提问来源于stack exchange,提问作者shiningStar
相关产品推荐
相关产品推荐

