疑问:springmvc 的配置项中<mvc:resources location="/static/" mapping="/static/**" cache-period="86400" />cache-period属性是怎样生效的?
...
Insight spring源码,按照以往的分析,mvc:xxx配置的解析由MvcNamespaceHandler完成。
/**
* NamespaceHandler for Spring MVC configuration namespace.
*
*/
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
}
}
...
springmvc 对静态资源提供了特定的RequestHandler: ResourceHttpRequestHandler。解析的cache-period做为Handler的cacheSeconds的值。
private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
// ...
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
// cache-period parse
String cacheSeconds = element.getAttribute("cache-period");
if (StringUtils.hasText(cacheSeconds)) {
// see WebContentGenerator.setCacheSeconds(int seconds)
resourceHandlerDef.getPropertyValues().add("cacheSeconds", cacheSeconds);
}
// ...
return beanName;
}
此时,cache-period解析完毕并且赋值给RequestHandler。那么RequestHandler的cacheSeconds作用是什么?
直接看实现,cacheSeconds体现在response 的header中,Tell browser直接用本地缓存吧。
public class ResourceHttpRequestHandler extends WebContentGenerator implements HttpRequestHandler, InitializingBean {
/**
* Processes a resource request.
*/
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
// Checks for supported methods and a required session, and applies the number of cache seconds.
checkAndPrepare(request, response, true);
// check whether a matching resource exists
Resource resource = getResource(request);
// check the resource's media type
MediaType mediaType = getMediaType(resource);
// header phase
setHeaders(response, resource, mediaType);
// content phase
writeContent(response, resource);
}
/**
* Apply the given cache seconds and generate respective HTTP headers.
* That is, allow caching for the given number of seconds in the
* case of a positive value, prevent caching if given a 0 value, else
* do nothing (i.e. leave caching to the client).
*/
protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
if (seconds > 0) {
// 我们的cacheSeconds是86400
cacheForSeconds(response, seconds, mustRevalidate);
}
else if (seconds == 0) {
preventCaching(response);
}
// Leave caching to the client otherwise.
}
/**
* Set HTTP headers to allow caching for the given number of seconds.
* Tells the browser to revalidate the resource if mustRevalidate is True.
*/
protected final void cacheForSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {
if (this.useCacheControlHeader) {
// HTTP 1.1 header
String headerValue = "max-age=" + seconds;
if (mustRevalidate || this.alwaysMustRevalidate) {
headerValue += ", must-revalidate";
}
response.setHeader(HEADER_CACHE_CONTROL, headerValue);
}
}
}