1.直接使用
springboot项目在启动类main方法下配置
@Bean
public ShallowEtagHeaderFilter shallowETagHeaderFilter() {
return new ShallowEtagHeaderFilter();
}
web项目在web.xml配置过滤器
<filter>
<filter-name>shallowEtagHeaderFilter</filter-name>
<filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</fliter-class>
</filter>
<filter-mapping>
<filter-name>shallowEtagHeaderFilter</filter-name>
<servlet-name>spring</servlet-name>
</filter-mapping>
由于我使用了spring的security会导致HTTP缓存失效 所有需要配置下
//在Spring Security配置类中配置如下代码 如下图
http.headers().cacheControl().disable();
在重写处理HttpSecurity配置方法中添加
2.查看效果
2.1 我写了个直接返回数据的接口
2.2 在浏览器第一次查询
2.3 第二次请求
可以看到请求标头和响应标头的ETag一致 响应码也为304 数据来自缓存
2.4 更改数据 再次请求
我更改了book_author改成如下
我们再次请求查看,数据已经发生变更请求标头与响应标头ETag不一致
3.控制器添加对HTTP缓存
上面这种使用方法 所有符合生成ETag条件的请求都会做缓存如果我需要对指定的URL做缓存呢?
如下 spring官网上的案例
详情参阅Spring Http缓存
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
Book book = findBook(id);
String version = book.getVersion();
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
.eTag(version) // lastModified is also available
.body(book);
}
4.原理解析
原理:Spring官方写的
翻译一下如下:
所述ShallowEtagHeaderFilter过滤器通过缓存写入响应的内容,并从它计算MD5哈希创建一个“浅”的ETag。客户端下次发送时,它会执行相同操作,但它也会将计算值与If-None-Match 请求标头进行比较,如果两者相等,则返回304(NOT_MODIFIED)
ShallowEtagHeaderFilter处理的核心源码
private void updateResponse(HttpServletRequest request, HttpServletResponse response) throws IOException {
//返回指定类型的适当响应对象
ContentCachingResponseWrapper responseWrapper =
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
//判断当前对象是否为null 等于null返回一个信息为“ContentCachingResponseWrapper not found”的非法参数异常
Assert.notNull(responseWrapper, "ContentCachingResponseWrapper not found");
//获取响应内容
HttpServletResponse rawResponse = (HttpServletResponse) responseWrapper.getResponse();
//获取响应状态码
int statusCode = responseWrapper.getStatusCode();
//判断提交的请求是否编写了响应状态和响应标头
if (rawResponse.isCommitted()) {
//最终调用response.flushBuffer()方法 将缓冲区的数据复制到响应当中
responseWrapper.copyBodyToResponse();
}
//判断给定的请求和响应是否符合生成ETag的条件
else if (isEligibleForEtag(request, responseWrapper, statusCode, responseWrapper.getContentInputStream())) {
//根据最新数据生成ETag
String responseETag = generateETagHeaderValue(responseWrapper.getContentInputStream(), this.writeWeakETag);
//把ETag放入响应标头中
rawResponse.setHeader(HEADER_ETAG, responseETag);
//获取从请求request携带过来的ETag
String requestETag = request.getHeader(HEADER_IF_NONE_MATCH);
//判断两个ETag是否相等
if (requestETag != null && ("*".equals(requestETag) || compareETagHeaderValue(requestETag, responseETag)))
//相等的话就返回一个304的响应码
rawResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
else {
//不相等就更新响应中的数据
responseWrapper.copyBodyToResponse();
}
}
else {
//上面两个条件都不符合,更新响应中的数据
responseWrapper.copyBodyToResponse();
}
}
5.作用
5.1优点:
- 用户访问快
- 节约服务器带宽
当响应为304的时候 浏览器并不需要从服务器拿取数据,直接从本地缓存中拿取,所以节省了时间及服务器带宽
缺点:不能节省CPU,java应用程序需要做的请求还是一样的,并且也需要进行数据库连接,服务器压力并没有降低