在初始化完成时,在上下文环境中已定义的所有 HandlerMapping 都已经被加载了,这些加载的 handlerMapping 被放在一个 List 中并被排序,存储着 HTTP 请求对应的映射数据。这个 List 中的每一个元素都对应着一个具体 handlerMapping 的配置,一般每一个 handlerMapping 可以持有一系列从 URL 请求到 Controller 的映射,而 Spring MVC 提供了一系列的 HandlerMapping 实现。如下:
通过这些在 HandlerMapping 中定义的映射关系,即这些 URL 请求和控制器的对应关系,使 Spring MVC 应用可以根据 HTTP 请求确定一个对应的 Controller。具体来说,这些映射关系是通过接口类 HandlerMapping 来封装的,在 HandlerMapping 接口中定义了一个 getHandler 方法,通过这个方法,可以获得与 HTTP 请求对应的 HandlerExecutionChain,而这个 HandlerExecutionChain,封装了具体的 Controller 对象:
public interface HandlerMapping { String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping"; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern"; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping"; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables"; String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables"; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes"; // 调用 getHandler 实际上返回的是一个 HandlerExecutionChain ,这是典型的 Command 的模式的使用,这个 HandlerExecutionChain // 不但持有 handler 本身,还包括了处理这个 HTTP 请求相关的拦截器 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
HandlerExecutionChain 看起来比较简洁,它持有一个 Interceptor 链和一个 handler 对象,这个 handler 对象实际上就是 HTTP 请求对应的 Controller,在持有这个 handler 对象的同时,还在 HandlerExecutionChain 中设置了一个拦截器链,通过这个拦截器链中的拦截器,可以为 handler 对象提供功能的增强。要完成这些工作,需要对拦截器链和 handler 都进行配置,这些配置都是在 HandlerExecutionChain 的初始化函数中完成的。为了维护这个拦截器链和 handler,handlerExecutionChain 还提供了一系列与拦截器链维护相关的一些操作,比如可以为拦截器链增加拦截器的 addInterceptor 方法等。
public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1; public HandlerExecutionChain(Object handler) { this(handler, (HandlerInterceptor[]) null); } public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<HandlerInterceptor>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } } public Object getHandler() { return this.handler; } public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList().add(interceptor); } public void addInterceptors(HandlerInterceptor... interceptors) { if (!ObjectUtils.isEmpty(interceptors)) { initInterceptorList().addAll(Arrays.asList(interceptors)); } } private List<HandlerInterceptor> initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); if (this.interceptors != null) { // An interceptor array specified through the constructor this.interceptorList.addAll(Arrays.asList(this.interceptors)); } } this.interceptors = null; return this.interceptorList; } public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; } 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; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } } void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { if (interceptors[i] instanceof AsyncHandlerInterceptor) { try { AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i]; asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler); } catch (Throwable ex) { logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex); } } } } } @Override public String toString() { if (this.handler == null) { return "HandlerExecutionChain with no handler"; } StringBuilder sb = new StringBuilder(); sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]"); if (!CollectionUtils.isEmpty(this.interceptorList)) { sb.append(" and ").append(this.interceptorList.size()).append(" interceptor"); if (this.interceptorList.size() > 1) { sb.append("s"); } } return sb.toString(); } }
下面我们以 SimpleUrlHandlerMapping 说明在实际中是如何应用的,SimpleUrlHandlerMapping 定义了一个 Map 变量(自己定义一个 Map 主要有两个作用,第一是方便配置,第二是可以在注册前做一些预处理,如确保所以 url 都以”/“开头),将所有的 url 和 Handler 的对应关系放在里面,最后注册到父类的 Map 中。
SimpleUrlHandlerMapping.java
public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } }
AbstractUrlHandlerMapping 对 handler 的注册。
AbstractUrlHandlerMapping.java
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
在各种准备工作完成后,就是使用 HandlerMapping 来完成请求的映射处理了,而具体执行过程是在 AbstactHandlerMapping 中的 getHandler 来完成的。
AbstactHandlerMapping.java
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); // 使用默认的 Handler,也就是"/"对应的 handler if (handler == null) { handler = getDefaultHandler(); } // 这里通过名称取出对应的 Handler Bean if (handler == null) { return null; } // Bean name or resolved handler? 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.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); 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); 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; }
在取得 handler 的具体过程在 getHandlerInternal 方法中实现。这个方法接受 HTTP 请求作为参数,它的实现在 AbstractUrlHandlerMapping 中,这个实现过程包括从 HTTP 请求中得到 URL ,并根据 URL 到 urlMapping 中获取 handler。
AbstractUrlHandlerMapping .java
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // 从request 中得到请求的 URL 路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 将得到的 URL 路径与 Handler 进行匹配 Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to " + handler); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; } // lookupHandler 根据 URL 路径启动在 HandlerMap 中对 handler 的检索,并最终返回 handler 对象 protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } // Pattern match? 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 bestPatternMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, patternComparator); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } bestPatternMatch = matchingPatterns.get(0); } if (bestPatternMatch != null) { handler = this.handlerMap.get(bestPatternMatch); if (handler == null) { Assert.isTrue(bestPatternMatch.endsWith("/")); handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1)); } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isDebugEnabled()) { logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables); } return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
至此,HTTP 请求的解析和匹配 Handler 过程就全部完成了,剩下的就是 HTTP 请求的分发处理了。
让我们重回到 DispatcherServlet,毫无疑问,它是 Spring MVC 中非常重要的一个类,他不仅仅建立了自己的持有 Ioc 容器,还肩负请求分布处理的任务。而 HTTP 的请求发布处理是在 doService 方法中完成的。
DispatcherServlet.java
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // 对 HTTP 请求参数进行处理 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 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 的分发请求的人口 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
对于请求的实际处理是由 doDispatch() 来完成的,这个方法很长,但过程简单明了。这个 doDispatch 方法是 DispatcherServlet 完成 Dispatcher 的主要方法,包括准备 ModelAndView,调用 getHandler 来响应 HTTP 请求,然后通过执行 Handler 的处理来得到返回的 ModelAndView 结果,最后把这个 ModelAndView 对象交给相应的视图对象去呈现。这里是 MVC 模式的核心地区:
DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); // 这里为视图准备好一个 ModelAndView,这个 ModelAndView 持有 handler 处理请求的结果 try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根据请求得到对应的 handler,handler 的注册以及 getHandler 的实现 mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 调用 Handler 的地方 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); // 判断是否需要进行视图的翻译和转换 if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
这里是与 MVC 框架最紧密相关的代码,得到 HTTP 请求对应的 HandlerExecutionChain ,执行 handler 并把模型数据展现到视图中去。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 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; }
其实,这个方法执行结束,MVC 框架中的 Controller 和 定义的一个拦截器链已经包含在其中了。然后,需要执行的就是返回适配的结果了。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { // 对持有的所有 adapter 进行匹配 for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } 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"); }
这里的 handler 是不是 Controller 接口的实现,可以通过 HandlerAdapter 来了解这个适配的过程。而具体的适配过程,我们以 SimpleControllerHandlerAdapter 的实现来说明。
public class SimpleControllerHandlerAdapter implements HandlerAdapter { // 判断将要调用的 handler 是不是 Controller. @Override public boolean supports(Object handler) { return (handler instanceof Controller); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } @Override public long getLastModified(HttpServletRequest request, Object handler) { if (handler instanceof LastModified) { return ((LastModified) handler).getLastModified(request); } return -1L; } }
得到 handler 对象,然后调用 handler 对象中的 HTTP 进行动作的响应。而具体的业务逻辑则被封装在 handler 中,由这些逻辑对 HTTP 请求进行相应的处理,从而生成所需要的数据,并把这些数据封装到 ModelAndView 对象中。最后,通过 handler 的 handlerRequest 方法触发完成,然后交给 MVC 的 View 部分处理。至此,Spring MVC 的主要逻辑就完成了。前面已经,分析了,MVC 的 C 和 M 部分。下面就是 View 部分的分析了。
——水门(2016年4月写于杭州)