概述
当我们在浏览器发起一个请求时,例如http://xxxx/home 跟http://xxxx/home.html时,后者会直接访问到相应的资源,而前者则会走Controller处理逻辑,最后再经过视图渲染(详情见:SpringMVC视图View解析源码分析)返回视图真正的资源,那么这其中的差别是什么?是哪里的代码来分别处理了这种不同的请求的?资源放置的位置又应该在哪里?
这一系列的问题在接下来的源码分析中将会得到解答。
请求处理流程
所有的被SpringMVC管理的资源在请求处理流程上首先都是通过DispatcherServlet,然后在这个servlet中获取Handler,然后再获取handlerAdapter,最后调用到真正的handler或者资源。这一块的详情逻辑可以查看:SpringMVC从配置初始化到HTTP请求全流程解析,这里就不再累述这个过程,我们直接进入代码的关键点DispatcherServlet->getHandler。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/***普通的Controller请求与静态资源请求处理的不同点就再这个地方被分开
即:根据request匹配到的Handler是不同的。***/
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
根据Debug代码发现:在handlerMappings列表里面依次有RequestMappingHandlerMapping, BeanNameHandlerMapping,SimpleUrlHandlerMapping。对于第一个我相信大家都不陌生,它就是处理Controller请求的入口,第二个很少会用到可以先不管,而最后一个就是帮助我们处理静态资源的HandlerMapping,因为静态资源是基于URL,所以统一由它来管理并返回对应的Handler. handlerMappings的初始化是在ApplicationContext中获取所有的实现了HandlerMapping的bean,所以我们可以预见到SimpleUrlHandlerMapping会在SpringMVC的某个配置类中被注入到Spring的容器中。 那么按照SpringMVC的配置风格,所有的配置应该会在WebMvcAutoConfiguration中完成,按照这个思路最终整理得到如下的类关系图。
在WebMvcConfigurationSupport中通过resourceHandlerMapping()方法配置并返回了HandlerMapping(这里其实就是SimpleUrlHandlerMapping),而该方法依赖于addResourceHandlers() 注入进来的handler。addResourceHandlers的具体实现是由DelegatingWebMvcConfiguration实现的,而它委托于WebMvcConfigurerComposite来完成用户自定义的resourceHandler配置,用户自定义配置之一就是WebMvcAutoConfigurationAdapter。
所以总结一下静态资源配置最核心的两个方法就是WebMvcConfigurationSupport中的resourceHandlerMapping和WebMvcAutoConfigurationAdapter中的addResourceHandlers。接下来进行代码分析。
代码分析
@Bean
@Nullable
public HandlerMapping resourceHandlerMapping(
@Qualifier("mvcUrlPathHelper") UrlPathHelper urlPathHelper,
@Qualifier("mvcPathMatcher") PathMatcher pathMatcher,
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider"