前言
这一篇,将着手介绍一次请求的处理。用到了 HandlerMapping、HandlerAdapter 知识,如果遇到不是太了解,可以回顾下。
源码分析
其实 DispatcherServlet 也只是 Servlet 的一个实现,只不过它集成了 SpringMVC 的几个功能组件(例如视图解析器),对请求及响应进行加工处理,所以探索一个 Servlet 实现,先从它的 service 方法实现开始,来看下 javax.servlet.http.HttpServlet 的实现。
public abstract class HttpServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
// 向下转型
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
// 根据请求方式调用不同的 doXxx方法
if (method.equals(METHOD_GET)) {
// 固定返回-1,说明缓存机制交由子类扩展
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
/**
* 缓存机制:首次请求后添加 “Last-Modefied” 的响应头,
* 第二次请求发送请求头 If-Modified-Since
* 如果服务端内容没有变化,则自动返回 304状态码
*/
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req, resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req, resp);
} else {
// 对于不支持请求方式,通过响应流输出错误
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
}
可以看到 service 就是做了向下转型,调用了 service(HttpServletRequest req, HttpServletResponse resp)。然后在里面根据请求方式,调用不同的 doXxx 方法。以上属于 servlet-api 自身的实现,接下来看看 SpringMVC 如何在此基础上改造。
// HttpServletBean是 HttpServlet子类
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
// 覆盖 service方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 支持 Patch请求或没有指定请求方法
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
// 不管什么请求,都会调用公共的处理方法
processRequest(request, response);
} else {
super.service(request, response);
}
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 请求开始时间,用于计算请求耗时
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 通过"accept-language"请求头构造 LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
// 获取当前线程请求的 RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes =
buildRequestAttributes(request, response, previousAttributes);
// 异步请求管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(
FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 将 LocaleContext、ServletRequestAttributes与当前线程绑定
initContextHolders(request, localeContext, requestAttributes);
try {
// 该类定义的抽象方法,子类 DispatcherServlet实现
doService(request, response);
}
.....// 省略 catch处理
finally{
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
.....// 省略日志
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
// 发布 ServletRequestHandledEvent事件
private void publishRequestHandledEvent( HttpServletRequest request,
HttpServletResponse response, long startTime, Throwable failureCause) {
// 默认为 true
if (this.publishEvents) {
// 请求耗时
long processingTime = System.currentTimeMillis() - startTime;
// 响应码
int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1);
// 封装了 请求的 url、remoteAddr、请求方式、sessionId、处理时间、响应码等信息
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, statusCode));
}
}
}
除了支持 servlet 自身支持的 7 种请求外,另外支持了 PATCH 方式请求。这里只是列举了 doGet、doPost,其实最终都调用了 processRequest 方法。
processRequest 方法通过调用 doService 来处理请求,在处理结束后发布了 ServletRequestHandledEvent 事件,可以自定义 ApplicationListener 来监听此事件。
doService
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.....// 省略日志
// 如果 attributes含有 “javax.servlet.include.request_uri”,保留属性的快照
// 用于支持 <jsp:incluede>
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
// 获取属性枚举
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
// 将属性压入map中
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 设置上下文、解析器等属性
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());
// 取出上一个请求的 FlashMap并给赋值给当前请求
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);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// 还原属性快照
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
}
DispatcherServlet 来进行主要的处理实现。doService 进行一些属性的设置之后,调用 doDispatch 方法进行处理
doDispatch
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 {
/**
* 调用我们配置的 MultipartResolver判断是否需要处理,如果没配置不会处理
* 以 CommonsMultipartResolver为例
* 检测 contentType是否为 multipart/form-data且必须是 POST请求
*/
processedRequest = checkMultipart(request);
// 如果满足上述条件,会包装成 DefaultMultipartHttpServletRequest
// 所以 multipartRequestParsed 为 true
multipartRequestParsed = (processedRequest != request);
// 根据 request找到对应的 Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 没找到 Handler,通过response响应 404错误信息
noHandlerFound(processedRequest, response);
return;
}
// 根据 Handler找到对应的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 对 last-modified 头支持:只有 Get 和 Head请求支持
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
....// 省略日志
// 对于缓存逻辑的判断,见 ServletWebRequest.checkNotModified
if (new ServletWebRequest(request, response).
checkNotModified(lastModified) && isGet) {
// 满足条件直接返回
return;
}
}
// 调用拦截器的 preHandle,返回true才能通过
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 调用 HandlerAdapter.handle返回处理后的 ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果没有 ModelAndView返回,则根据请求默认给出一个视图名称
applyDefaultViewName(processedRequest, mv);
// 调用拦截器的 postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 视图处理(包含异常视图),最后会调用拦截器的 triggerAfterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// 调用拦截器的 triggerAfterCompletion
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
// 调用拦截器的 triggerAfterCompletion
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
// 调用 AsyncHandlerInterceptor.afterConcurrentHandlingStarted
// 用于在异步请求处理开始之后回调
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// 如果是文件上传请求,需要清理资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
通过层层的调用,终于来到了 doDispatch ,这个方法大致能够看到请求的处理步骤概览:
- 首先判断是否为文件上传的请求:和普通的请求不一样,这种请求需要在请求结束后清理文件解析过程中创建的 MultipartFile (可能会在磁盘上保留有临时数据);
- 获取 Handler :从 HandlerMapping初始化建立的映射关系中找出;
- 拦截器 HandlerInterceptor.preHandle 调用;
- HandlerAdapter 适配:从 HandlerAdapter 初始化注册的所有适配器中,找到支持对应 Handler 的适配器,调用 handle 方法处理(见 HandlerAdapter)
- 拦截器 HandlerInterceptor.postHandle 调用;
- 调用 processDispatchResult 进行视图处理;
- 拦截器 HandlerInterceptor.afterCompletion 调用(异常同样会触发,并且会作为参数传入)
接下来我们来看几个主要步骤的解析。
getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历所有注册的 HandlerMapping
for (HandlerMapping hm : this.handlerMappings) {
....// 省略日志
// 找到匹配的HandlerExecutionChain(不为null)
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
调用 HandlerMapping.getHandler 获取请求对应的 Handler。来看实现:
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 根据 request获取对应的 Handler(子类实现)
Object handler = getHandlerInternal(request);
// 如果没有找到对应 Handler,使用默认Handler
if (handler == null) {
handler = getDefaultHandler();
}
// 如果默认的 Handler没有,则返回 null
if (handler == null) {
return null;
}
// Handler为 String类型,说明是beanName,调用 getBean获取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 执行链构造:Handler和拦截器
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 判断跨域请求:是否带有“Origin”请求头
if (CorsUtils.isCorsRequest(request)) {
// 全局跨域配置
CorsConfiguration globalConfig =
this.globalCorsConfigSource.getCorsConfiguration(request);
// 单个 Handler配置(注解 @CrossOrigin其实就是对单个 Handler的配置)
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
// 配置合并
CorsConfiguration config = (globalConfig != null ?
globalConfig.combine(handlerConfig) : handlerConfig);
// 添加 CorsInterceptor拦截器(使用合并后的配置)
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
// 执行链构造
protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
// 遍历 List<HandlerInterceptor>
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
// 将匹配的 MappedInterceptor加入处理链
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
// 其他的直接加入
chain.addInterceptor(interceptor);
}
}
return chain;
}
// 跨域拦截器加入
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, CorsConfiguration config) {
// 条件:带有“Origin”、“Access-Control-Request-Method”请求头的 options请求
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = chain.getInterceptors();
// 使用 PreFlightHandler替代原本的 Handler处理请求
chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
} else {
// 添加 CorsInterceptor拦截器(拦截器末尾)
chain.addInterceptor(new CorsInterceptor(config));
}
return chain;
}
}
抽象父类 AbstractHandlerMapping 实现了 执行链的构造 以及 “跨域”相关处理(拦截器),查找 Handler 的逻辑交由子类实现(getHandlerInternal)。回想一下 HandlerMapping 注册 Handler 的逻辑分为了两个分支 AbstractUrlHandlerMapping 和 AbstractHandlerMethodMapping (见 HandlerMapping 初始化),因此查找逻辑也随注册逻辑不同而不同。
AbstractUrlHandlerMapping 分支
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 这一步我们会取到截取后的请求相对地址
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 根据相对地址找到对应的 Handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// 没有的话,依次查询根 Handler、默认 Handler
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// 如果 Handler是 beanName,调用 getBean获取
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
// 校验:由 DefaultAnnotationHandlerMapping实现
// 看请求是否满足 @RequestMapping指定的 method、param、header
validateHandler(rawHandler, request);
// 使用 Handler创建执行链,链头添加 PathExposingHandlerInterceptor拦截器
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
....// 省略日志
return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 从映射关系中找出请求对应的 Handler(直接路径)
// 映射关系的初始化见 HandlerMapping初始化
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// beanName以及 Handler校验,上面讲过了
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
// 构造执行链
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 直接路径中未找到,使用通配符匹配
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
} else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") &&
getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
// 使用模式匹配后,查找最匹配的 Handler
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
....// 省略日志
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// beanName以及 Handler校验,上面讲过了
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// 可能存在多个“最佳模式”,让我们确保所有这些模式都有正确的URI模板变量
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
....// 省略日志
// 构造执行链
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// 没有匹配的 Handler返回 null
return null;
}
}
首先调用 UrlPathHelper.getLookupPathForRequest 获取请求的相对路径。以 Tomcat 举例,配置的 <Context path="xxx"> 项目根路径,那么对应的 web 应用所有的请求,都要添加 “xxx” 前缀,但我们的应用对此是无感知的,所以框架层面要把这些截取后,再去查找 Handler。来看下截取逻辑:
public class UrlPathHelper {
public String getLookupPathForRequest(HttpServletRequest request) {
// 默认为 false
if (this.alwaysUseFullPath) {
return getPathWithinApplication(request);
}
String rest = getPathWithinServletMapping(request);
if (!"".equals(rest)) {
return rest;
} else {
return getPathWithinApplication(request);
}
}
public String getPathWithinServletMapping(HttpServletRequest request) {
// 获取截取后的相对地址
String pathWithinApp = getPathWithinApplication(request);
// 获取的 <servlet>指定的 <url-pattern>(移除通配符后)
String servletPath = getServletPath(request);
// 把 pathWithinApp中的“//”替换成“/”
String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp);
String path;
// 这一步主要就是对请求地址中多余的“/”进行移除匹配
if (servletPath.contains(sanitizedPathWithinApp)) {
// 同样的,把请求的相对地址中,servletPath截取掉
path = getRemainingPath(sanitizedPathWithinApp, servletPath, false);
} else {
// 同样的,把请求的相对地址中,servletPath截取掉
path = getRemainingPath(pathWithinApp, servletPath, false);
}
if (path != null) {
return path;
} else {
// 如果请求不在 servlet指定的 <url-pattern>下
String pathInfo = request.getPathInfo();
if (pathInfo != null) {
return pathInfo;
}
if (!this.urlDecode) {
path = getRemainingPath(decodeInternal(request, pathWithinApp), servletPath, false);
if (path != null) {
return pathWithinApp;
}
}
return servletPath;
}
}
public String getPathWithinApplication(HttpServletRequest request) {
// 获取的项目根路径
String contextPath = getContextPath(request);
// 获取请求的相对地址
String requestUri = getRequestUri(request);
// 把请求的相对地址中的项目根路径截去
String path = getRemainingPath(requestUri, contextPath, true);
if (path != null) {
return (StringUtils.hasText(path) ? path : "/");
} else {
return requestUri;
}
}
}
(getPathWithinServletMapping)首先会在配置的 DispatcherServlet 范围内查找,对于同一个请求 “http://localhost/a/b” 来说:
- <url-pattern>/*</url-pattern>, 返回的就是 “/a/b”;
- <url-pattern>/a/*</url-pattern>,那么返回的就是 “/b”。如果请求为 “http://localhost/a” ,那么返回的就是空字符串了。
(getPathWithinApplication)只有在上一步返回空字符串时才会在 Application 范围内查找,对于用一个请求“http://localhost/context/a” 来说:
- <Context path="/context">,返回的就是 “/a”;
- <Context path="/">,返回的就是 “/context/a”。如果请求为 “http://localhost” ,那么返回的就是 “/”了。
AbstractHandlerMethodMapping 分支
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 同样使用 UrlPathHelper.getLookupPathForRequest
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
this.mappingRegistry.acquireReadLock();
try {
// 根据请求 url找到对应的 HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
....// 省略日志
// createWithResolvedBean目的是确保 HandlerMethod中持有的是被调用的实例 bean
// 而不是 beanName,如果是会调用了 getBean获取实例后创建新的 HandleMethod
// 因为这个实例 bean会用于反射调用方法
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
} finally {
this.mappingRegistry.releaseReadLock();
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// 使用 MappingRegistry.getMappingsByUrl,通过相对路径查找对应的 List<RequestMappingInfo>
// 见 AbstractHandlerMethodMapping的注册逻辑
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 不为 null,说明 @RequestMapping指定的是非通配符路径
// 找到匹配条件的填充 matches
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 如果通过直接路径找不到,就在所有注册的映射路径中查找
// 找到匹配条件的填充 matches
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);
if (matches.size() > 1) {
// 条件:带有“Origin”、“Access-Control-Request-Method”请求头的 options请求
if (CorsUtils.isPreFlightRequest(request)) {
// 返回 EmptyHandler封装的 HandlerMethod
// 调用会抛出异常 UnsupportedOperationException
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 + "}");
}
}
// 调用 setAttribute设置一些属性
handleMatch(bestMatch.mapping, lookupPath, request);
// 返回匹配的 HandlerMethod
return bestMatch.handlerMethod;
} else {
// 未匹配
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
}
同样使用的是 UrlPathHelper.getLookupPathForRequest 获取请求的相对路径。之后根据相对路径找到对应的 HandlerMethod 。涉及到了一个请求地址对应了多个匹配结果的筛选:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches,
HttpServletRequest request) {
for (T mapping : mappings) {
// 获取满足条件的 RequestMappingInfo
T match = getMatchingMapping(mapping, request);
// 只有满足返回的才不为 null,这一步会筛选掉不符合条件的
// 例如:请求路径不匹配、header头不匹配等等
if (match != null) {
// 通过 MappingRegistry维护的 mappingLookup找到对应的 HandlerMethod
// 使用 RequestMappingInfo和 HandlerMethod创建 Match
// 用于 MatchComparator的排序
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
public abstract class RequestMappingInfoHandlerMapping extends
AbstractHandlerMethodMapping<RequestMappingInfo> {
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
// 调用 RequestMappingInfo.getMatchingCondition
return info.getMatchingCondition(request);
}
}
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
@Override
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
// 这一步基本不会,因为 @RequestMapping都有默认值
if (methods == null || params == null || headers == null
|| consumes == null || produces == null) {
return null;
}
// 对于 @RequestMapping指定的 path/value匹配(直接地址匹配、通配符匹配)
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
// 对于 @RequestMapping指定的 method、param、header、consumes、produces的匹配
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
}
以上的源码,就是用满足请求条件的(指定的 method、header等) RequestMappingInfo 以及对应的 HandlerMethod 封装成 Match, 并填充到 matches 的逻辑。
这一步可能会筛选出多个匹配项,接下来就需要靠 MatchComparator 排序后挑选出最匹配项,逻辑见 RequestMappingInfo.compareTo,这里不展开分析。
getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历所有注册的 HandlerAdapter
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
// 调用 HandlerAdapter.supports看是否支持适配
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");
}
这里调用的 supports 获取到支持适配的 HandlerAdapter,之后调用其 handle 方法执行处理逻辑,具体源码上篇已分析,见 “HandlerAdapter”。
执行完之后,就获取到了 ModelAndView 返回,接着就是对视图的处理(processDispatchResult),放在下节分析。
总结
本篇分析的就是一个请求从接收到处理的全过程,目前进度已经获取到了 ModelAndView,至于请求地址如何找到对应的 Handler、Handler 如何调用并返回 ModelAndView ,前几篇已作讲解。下篇将分析 “视图处理” 源码。