参考资料:
《SpringMVC源码解析系列》
《SpringMVC源码分析》
《Spring MVC源码》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
前文:
《SpringMVC源码:DispatcherServlet初始化流程》
《SpringMVC源码:HandlerMapping加载1》
《SpringMVC源码:HandlerMapping加载2》
在前文中我们介绍了DispatcherServlet的初始化以及基础组件HandlerMapping的初始化工作,在这之后DispatcherServlet的doService方法就可以开始提供服务了。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//其余代码
//为request设置额外的属性信息
/WebApplicationContext、localeResolver、themeResolver、ThemeSource
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//设置重定向传参相关
//INPUT_FLASH_MAP 用于保存上一个请求重定向过来的参数
//OUTPUT_FLASH_MAP 用于保存重定向到别的请求需要传递的参数
//flashMapManager 用于支持重定向传参的
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,Collections.unmodifiableMap(inputFlashMap) );
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//具体执行针对不同请求进行处理的核心方法
doDispatch(request, response);
}
}
doService内部首先进行了部分属性设置,然后就是调用doDispatch实现对不同请求的分发处理,doDispatch的过程可以大致概括为handler查找、适配器查找、处理器调用、视图渲染。
本文我们将介绍handler处理与其适配器的查找,以及方法调用前的拦截器处理。(本文是基于之前文章的基础上开始讲解的,如果对SpringMVC不熟悉的朋友建议先阅读前文。)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查是否为Multipart请求,如果是则HttpServletRequest转换为MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//1.根据request信息寻找对应的Handler
//并返回mappedHeadler,它是一个执行链,包含拦截器和自定义controller
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//没有找到Handler则通过response反馈错误信息
noHandlerFound(processedRequest, response);
return;
}
// 2.根据当前的handler寻找对应的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 当前handler处理last-modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//3.mappedHandler调用拦截器的preHandler方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 4.HandlerAdapter调用自定义Controller中的方法并返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//5.视图解析,为request请求找到视图
applyDefaultViewName(processedRequest, mv);
//6.调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
目录
一、getHandler
1、 DispatcherServlet#getHandler
2、AbstractHandlerMapping#getHandler
2.1、AbstractHandlerMethodMapping#getHandlerInternal
2.2、AbstractHandlerMethodMapping#lookupHandlerMethod
2.3、RequestMappingInfoHandlerMapping#handleMatch
3、AbstractHandlerMapping#getHandlerExecutionChain
二、getHandlerAdapter
为什么需要HandlerAdapter适配器?
1、DispatcherServlet#getHandlerAdapter
2、supports
2.1、AbstractHandlerMethodAdapter#supports
2.2、SimpleControllerHandlerAdapter#supports
三、applyPreHandle
preHandle
postHandle
afterCompletion
HandlerExecutionChain#applyPreHandle
一、getHandler
1、 DispatcherServlet#getHandler
getHandler方法就是从HandlerMapping中查询匹配当前request的Handler。只要一匹配上 handler 就不再循环,直接返回。
这里实际上是调用的HandlerMapping接口的抽象基类AbstractHandlerMapping中的getHandler方法,然后利用模板模式调用每个实现类判断是否匹配该Handler。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//成员变量handlerMappings,前文中已初始化
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
2、AbstractHandlerMapping#getHandler
这里的主要作用是根据请求获取包装了Handler对象和拦截器的HandlerExecutionChain对象。该方法采用模板方法获取拦截器处理器链。
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//交由子类实现的根据请求获取处理器的具体逻辑 不同的子类获取Handler的逻辑不同
Object handler = getHandlerInternal(request);
//如果没有获取到Handler 则获取默认的Handler
//默认的处理器可以通过配置映射器实例的时候通过属性设置
if (handler == null) {
handler = getDefaultHandler();
}
//如果默认的Handler还是没有则返回空
if (handler == null) {
return null;
}
//Handler处理器是字符串类型 说明获取的Handler是beanName
//则从spring 容器中获取bean实例对象
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//为Handler添加拦截器,并最终返回HandlerExecutionChain对象
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
2.1、AbstractHandlerMethodMapping#getHandlerInternal
getHandlerInternal由AbstractHandlerMapping的各个子类实现,这里以AbstractHandlerMethodMapping为例进行介绍(AbstractUrlHandlerMapping中的实现可以看我之前这篇文章《SpringMVC源码:HandlerMapping加载1》)。
我们可以看到第一步AbstractHandlerMethodMapping与AbstractUrlHandlerMapping中的处理都是先解析出请求的url,然后再根据这个请求url查找对应的handler/HandlerMethod。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//通过UrlPathHelper对象从request解析出urlPath
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
//查询匹配的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
// 对HandlerMethod进行实例赋值(因为HanlerMethod中的bean属性是该方法对应对应的类对象
// 这里通过createWithResolvedBean会将spring容器中的HandlerMethod所属的bean实例赋值
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
2.2、AbstractHandlerMethodMapping#lookupHandlerMethod
该方法负责根据请求request和对应的请求路径lookupPath获取对应的HandlerMethod。
如果没有精确匹配,则对查询结果进行排序后选出最佳匹配。如果无法匹配到对应的映射俄关系,则spring提供handleNoMatch 进行处理,获取到不完全匹配映射则根据该映射,.提示改客户请求与该映射接近的请求出现不完全匹配的原因。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// 从mappingRegistry获取匹配到的RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
//找到完全匹配的则将mappings、handlerMethod 添加到matches中
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//如果没有精确匹配 则这里将所有映射关系和HandlerMethod封装成Match添到matchs中
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// 对匹配项进行排序
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
//如有多个Match符合要求 这里需要获取最佳匹配的Match
//获取映射关系的比较器 主要是比较RequestMappingInfo的匹配度 因为包含RequestMappingInfo
// 包含不同类型的RequestCondtion 比较逻辑是这些RequestCondtion的compareTo()依次比较
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
// 比较最佳匹配有多条相同则抛出异常
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
//将匹配的HandlerMethod 存放到request中 方便后期使用
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
//获取的请求mapping进行处理(主要是针对pattern类型进行 请求路径url和请求参数的解析存放request)
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// 无匹配项处理
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
2.3、RequestMappingInfoHandlerMapping#handleMatch
在获取到了最佳的Match(包含RequestMappingInfo,handlerMethod)对象后,对其中pattern类型的@RequestMapping修饰的方法进行参数和请求pattern、真实路径path的解析,为后续的便于使用将这些参数作为属性存放到request对象。
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
//调用父类的方法 request存储lookupPath 请求路径
super.handleMatch(info, lookupPath, request);
String bestPattern;
Map<String, String> uriVariables;
Map<String, String> decodedUriVariables;
//如果不是pattern模式 则直接将请求路径作为pattern存储 参数为null
//比如请求 localhost:port/context/book/bookListPage
Set<String> patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
decodedUriVariables = Collections.emptyMap();
}
//对于pattern模式 比如请求localhost:port/context/book/book/{id} 则为pattern模式请求
//对于真实请求路径可能是这样 localhost:port/context/book/book/2243
//该请求查询最好最低以及品牌类型
else {
//获取其中的pattern
bestPattern = patterns.iterator().next();
//根据pattern从lookupPath获取参数 上面的例子就获取到了参数2243
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
//针对路径请求进行编码后存储到request
decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
}
//存储pattern到request
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
if (isMatrixVariableContentAvailable()) {
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
//如果方法的@RequestMapping修饰的方法包含produces属性 其中的context-type 中的mediaTypes存储到request
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
3、AbstractHandlerMapping#getHandlerExecutionChain
查询匹配的拦截器,组装Handler生成HandlerExecutionChain。这里的adaptedInterceptors在我们的前文中已经介绍过了,就不重复了。
该方法内将所有的拦截器通过责任链的模式组装到了一起,当调用该handler时都会通过这个责任链执行拦截器内的处理方法。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//判断当前handler是否为拦截器,不是的话就创建一个
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//获取到请求的url
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
//将拦截器加入到拦截器链中
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
到这里为止整个getHandler的调用就结束了,可以看到整个方法过程就是先去HandlerMapping中找到对应请求的处理器handler,并组装拦截器链后返回。
二、getHandlerAdapter
上一节我们通过HandlerMapping的映射关系找到了对应请求的handler处理器,本节我们将对完成对其的适配器查找。
为什么需要HandlerAdapter适配器?
在前文中我们了解到了handler对象有不同的类型
(1)以实现了Controller接口的Handler类
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("进入DemoController方法执行处理逻辑");
return new ModelAndView("demo");
}
}
(2)以@RequestMapping注解修饰的HandlerMethod对象
@RequestMapping(value = "/book/ListPage",method = RequestMethod.POST)
@ResponseBody
public String getBookPage(@RequestBody BookQuery bookQuery){
return success(pageTotal(pageInfo));
}
其他还有实现Servlet的实例,HandlerFunction实例、HttpRequestHandler实例等,不同的实例对象调用时走不同的方法,为了能将不同的方法转换成统一的调用形式,这里使用了适配器模式,将各个实例的方法调用包装到HandlerAdapter统一调用。
1、DispatcherServlet#getHandlerAdapter
该方法遍历所有的HandlerAdapter,找出支持解析传入的handler对象的实现类。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
//调用HandlerAdapter.support()方法 判断是否支持该handler对象的解析
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
2、supports
2.1、AbstractHandlerMethodAdapter#supports
以抽象类AbstractHandlerMethodAdapter为例,该类中的supports实际上就是判断handler对象是否为HandlerMethod类型,换句话说只要是HandlerMethod类型的处理器该适配器都支持。
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
//RequestMappingHandlerAdapter.java
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
2.2、SimpleControllerHandlerAdapter#supports
再看另一种类型SimpleControllerHandlerAdapter,就变成了支持Controller类型的handler。
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
三、applyPreHandle
回到doDispatch方法,在handler处理器执行前,会先调用applyPreHandle,执行HandlerExecutionChain 中所有拦截器的preHandle()方法。
// 拦截器的前置处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
拦截器是将很多service或者Controller中共有的行为提炼出来,在某些方法执行的前后执行,提炼为通用的处理方式,让被拦截的方法都能享受这一共有的功能,让代码更加简洁,同时,当共有的功能需要发生调整、变动的时候,不必修改很多的类或者方法,只要修改这个拦截器就可以了,可复用性很强。
拦截器主要有三个方法:
preHandle
该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。
该方法的返回值是布尔值Boolean类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
postHandle
在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。
postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
afterCompletion
该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
HandlerExecutionChain#applyPreHandle
这里获取到我们本文第一步中添加到责任链里的拦截器,然后依次执行他们的preHandle方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
public HandlerInterceptor[] getInterceptors() {
if (this.interceptors == null && this.interceptorList != null) {
this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
}
return this.interceptors;
}
本文我们介绍了请求分发核心逻辑doDispatch方法过程中的handler查找、适配器查找以及拦截器preHandle方法的执行,下篇文章我们将会继续介绍方法的调用。