springmvc源码学习(十二)缓存Last-Modified


前言

本文分析常用的@Controller中方法中实现Last-Modified的功能


一、引入

RequestMappingHandlerAdapter.java

	/**
	 * This implementation always returns -1. An {@code @RequestMapping} method can
	 * calculate the lastModified value, call {@link WebRequest#checkNotModified(long)},
	 * and return {@code null} if the result of that call is {@code true}.
	 */
	@Override
	protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
		return -1;
	}

从注释可以看出,RequestMappingHandlerAdapter的获取最后修改时间方法返回值是-1,即默认没有实现lastModified的功能;要想实现lastModified 缓存功能 ,@RequestMapping 方法可以通过WebRequest#checkNotModified(long)方法计算最后修改时间,如果请求的资源没有修改,则目标方法返回null值即可。

二、使用

定义一个@GetMapping方法,并调用WebRequest#checkNotModified(long)方法,即可实现Last-Modified的功能。

	private long lastModified = System.currentTimeMillis();

	@GetMapping(value = "/checkNotModified", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity checkNotModified(WebRequest request) {

        if (request.checkNotModified(lastModified)) {
            System.out.println("lastModified : " + lastModified);
            return null;
        }
        return new ResponseEntity();
    }

三、原理分析

从WebRequest#checkNotModified(long)方法开始

(1)checkNotModified( )

ServletWebRequest.java

public boolean checkNotModified(long lastModifiedTimestamp) {
		return checkNotModified(null, lastModifiedTimestamp);
	}
public boolean checkNotModified(@Nullable String etag, long lastModifiedTimestamp) {
		HttpServletResponse response = getResponse();
		if (this.notModified || (response != null && HttpStatus.OK.value() != response.getStatus())) {
			return this.notModified;
		}

		// Evaluate conditions in order of precedence.
		// See https://tools.ietf.org/html/rfc7232#section-6
		
		//检测If-Unmodified-Since,是否没有修改过
		if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
			if (this.notModified && response != null) {
				//设置412状态码
				response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
			}
			return this.notModified;
		}
		
		//检测If-None-Match
		boolean validated = validateIfNoneMatch(etag);
		if (!validated) {
			//检测If-Modified-Since,是否修改过
			validateIfModifiedSince(lastModifiedTimestamp);
		}

		// Update response
		if (response != null) {
			boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
			if (this.notModified) {
				//没有修改过,并且是get或head请求,设置状态码304
				response.setStatus(isHttpGetOrHead ?
						HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
			}
			if (isHttpGetOrHead) {
				if (lastModifiedTimestamp > 0 && parseDateValue(response.getHeader(HttpHeaders.LAST_MODIFIED)) == -1) {
					//设置Last-Modified为最后修改时间lastModifiedTimestamp
					response.setDateHeader(HttpHeaders.LAST_MODIFIED, lastModifiedTimestamp);
				}
				if (StringUtils.hasLength(etag) && response.getHeader(HttpHeaders.ETAG) == null) {
					//设置Etag
					response.setHeader(HttpHeaders.ETAG, padEtagIfNecessary(etag));
				}
			}
		}

		return this.notModified;
	}

(2)validateIfUnmodifiedSince( )

private boolean validateIfUnmodifiedSince(long lastModifiedTimestamp) {
		if (lastModifiedTimestamp < 0) {
			return false;
		}
		//解析出If-Unmodified-Since
		long ifUnmodifiedSince = parseDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
		if (ifUnmodifiedSince == -1) {
			return false;
		}
		// We will perform this validation...
		//notModified 为true时,表明被修改过,响应中设置412状态码
		//notModified 为false时,表明没有被修改过
		this.notModified = (ifUnmodifiedSince < (lastModifiedTimestamp / 1000 * 1000));
		return true;
	}

(3)validateIfModifiedSince( )

private boolean validateIfModifiedSince(long lastModifiedTimestamp) {
		if (lastModifiedTimestamp < 0) {
			return false;
		}
		//解析出If-Modified-Since
		long ifModifiedSince = parseDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
		if (ifModifiedSince == -1) {
			return false;
		}
		// We will perform this validation...
		//notModified 为true时,表明没有被修改过,响应中设置403状态码
		//notModified 为false时,表明被修改过
		this.notModified = ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000);
		return true;
	}

总结:

Last-Modified存在的问题:
1、lastModifiedTimestamp / 1000 * 1000,可以看出资源在1秒之内发生变化时,系统可能察觉不出来;
2、文件周期生成,内容没有发生变化时,请求时系统会重新获取资源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_lrs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值