阅读本文章大概需要8分钟左右。相信会让你对Spring MVC的理解更加深刻,更上一层楼。
粒度很粗的图解
粒度比较粗的图解
粒度一般的图解
FrameworkServlet是Spring MVC框架中的基本Servlet,集成提供了Spring应用的上下文。通过读取我们在web.xml中配置的ContextConfigLocation、ContextLoaderListener、ContextClass属性注入上下文。子类必须重写doService()方法去处理请求。
假如我们要请求http://localhost:8081/order/detail?orderId=1,由于我们的请求方式是GET,会进入到doGet()方法。实际上这个方法会把请求委托给processRequest()和doService()处理。
/** * Delegate GET requests to processRequest/doService. * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead}, * with a {@code NoBodyResponse} that just captures the content length. * @see #doService * @see #doHead */ @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
在processRequest()方法中,会处理这个请求,并且不管结果如何,都会发布一个请求事件。实际上处理请求是子类DispatcherServlet的doService()方法完成的。
/** * Process this request, publishing an event regardless of the outcome. * <p>The actual event handling is performed by the abstract * {@link #doService} template method. */ protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } }
DispatcherServlet就是一个前端控制器,集中提供请求处理机制。将url映射到指定的Controller处理,Controller处理完毕后将ModelAndView返回给DispatcherServlet,DispatcherServlet通过viewResovler进行视图解析,然后将model填充到view,响应给用户。
doService()方法会将判断该请求是不是包含请求。如果是包含请求,会将request对象的参数进行快照,以便在包含后恢复这些属性。这些属性分别是
javax.servlet.include.request_uri javax.servlet.include.context_path javax.servlet.include.servlet_path javax.servlet.include.path_info javax.servlet.include.query_string
接着将Spring MVC框架的全局对象注入到request对象中,让handler和view对象可用。接着调用doDispatch()方法
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(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. 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(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()这个方法很核心,把请求调度给真正的handler去处理。
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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 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; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", 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); } } } }
checkMultipart(request)判断这个请求是否是Multipart,比如文件上传就是Multipart请求。如果是Multipart请求就交给multipartResolver处理,如果不是Multipart返回当前的请求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " + "this typically results from an additional MultipartFilter in web.xml"); } else if (hasMultipartException(request) ) { logger.debug("Multipart resolution failed for current request before - " + "skipping re-resolution for undisturbed error rendering"); } else { try { return this.multipartResolver.resolveMultipart(request); } catch (MultipartException ex) { if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) { logger.debug("Multipart resolution failed for error dispatch", ex); // Keep processing error dispatch with regular request handle below } else { throw ex; } } } } // If not returned before: return original request. return request; }
multipartRequestParsed = (processedRequest != request)判断处理后的请求是否和处理前的请求一致。如果不一致,multipartRequestParsed标志为true,代表这个请求已经被multipartResolver处理过了。
// Determine handler for the current request. mappedHandler = getHandler(processedRequest);
在getHandler(processedRequest)里面通过遍历所有的handlerMapping,调用handlerMapping对象中的getHandler(request)方法获得HandlerExecutionChain对象。实际上这里的handlerMapping对象是RequestMappingHandlerMapping对象。
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; }
进入到AbstractHandlerMapping中的getHandler(request),一看究竟。handler是通过getHandlerInternal(request)获得的。
/** * Look up a handler for the given request, falling back to the default * handler if no specific one is found. * @param request current HTTP request * @return the corresponding handler instance, or the default handler * @see #getHandlerInternal */ @Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } 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; }
进入到AbstractHandlerMethodMapping中的getHandlerInternal(request)方法,先从request对象获取当前要查询的lookupPath。
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); try { 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 + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
这里的mappingRegistry说白了就是一个映射关系注册中心,里面维护了所有mapping到处理程序handlerMethod的映射关系,以便查找和提供并发访问。所以每次通过访问显式获得锁,访问结束后要显式释放锁。
/** * A registry that maintains all mappings to handler methods, exposing methods * to perform lookups and providing concurrent access. * * <p>Package-private for testing purposes. */ class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<String, List<HandlerMethod>>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<HandlerMethod, CorsConfiguration>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** * Return all mappings and handler methods. Not thread-safe. * @see #acquireReadLock() */ public Map<T, HandlerMethod> getMappings() { return this.mappingLookup; } /** * Return matches for the given URL path. Not thread-safe. * @see #acquireReadLock() */ public List<T> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); } /** * Return handler methods by mapping name. Thread-safe for concurrent use. */ public List<HandlerMethod> getHandlerMethodsByMappingName(String mappingName) { return this.nameLookup.get(mappingName); } /** * Return CORS configuration. Thread-safe for concurrent use. */ public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) { HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod(); return this.corsLookup.get(original != null ? original : handlerMethod); } /** * Acquire the read lock when using getMappings and getMappingsByUrl. */ public void acquireReadLock() { this.readWriteLock.readLock().lock(); } /** * Release the read lock after using getMappings and getMappingsByUrl. */ public void releaseReadLock() { this.readWriteLock.readLock().unlock(); } public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped /"" + mapping + "/" onto " + handlerMethod); } this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } } private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) { HandlerMethod handlerMethod = this.mappingLookup.get(mapping); if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) { throw new IllegalStateException( "Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method /n" + newHandlerMethod + "/nto " + mapping + ": There is already '" + handlerMethod.getBean() + "' bean method/n" + handlerMethod + " mapped."); } } private List<String> getDirectUrls(T mapping) { List<String> urls = new ArrayList<String>(1); for (String path : getMappingPathPatterns(mapping)) { if (!getPathMatcher().isPattern(path)) { urls.add(path); } } return urls; } private void addMappingName(String name, HandlerMethod handlerMethod) { List<HandlerMethod> oldList = this.nameLookup.get(name); if (oldList == null) { oldList = Collections.<HandlerMethod>emptyList(); } for (HandlerMethod current : oldList) { if (handlerMethod.equals(current)) { return; } } if (logger.isTraceEnabled()) { logger.trace("Mapping name '" + name + "'"); } List<HandlerMethod> newList = new ArrayList<HandlerMethod>(oldList.size() + 1); newList.addAll(oldList); newList.add(handlerMethod); this.nameLookup.put(name, newList); if (newList.size() > 1) { if (logger.isTraceEnabled()) { logger.trace("Mapping name clash for handlerMethods " + newList + ". Consider assigning explicit names."); } } } public void unregister(T mapping) { this.readWriteLock.writeLock().lock(); try { MappingRegistration<T> definition = this.registry.remove(mapping); if (definition == null) { return; } this.mappingLookup.remove(definition.getMapping()); for (String url : definition.getDirectUrls()) { List<T> list = this.urlLookup.get(url); if (list != null) { list.remove(definition.getMapping()); if (list.isEmpty()) { this.urlLookup.remove(url); } } } removeMappingName(definition); this.corsLookup.remove(definition.getHandlerMethod()); } finally { this.readWriteLock.writeLock().unlock(); } } private void removeMappingName(MappingRegistration<T> definition) { String name = definition.getMappingName(); if (name == null) { return; } HandlerMethod handlerMethod = definition.getHandlerMethod(); List<HandlerMethod> oldList = this.nameLookup.get(name); if (oldList == null) { return; } if (oldList.size() <= 1) { this.nameLookup.remove(name); return; } List<HandlerMethod> newList = new ArrayList<HandlerMethod>(oldList.size() - 1); for (HandlerMethod current : oldList) { if (!current.equals(handlerMethod)) { newList.add(current); } } this.nameLookup.put(name, newList); } }
我们继续回到AbstractHandlerMethodMapping中的getHandlerInternal(request)方法中,通过调用this.mappingRegistry.acquireReadLock()方法申请获得mapping注册中心中的读锁。
this.mappingRegistry.acquireReadLock();
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
接着调用lookupHandlerMethod(lookupPath, request),通过url匹配的方式获得合适的hanlderMethod。
/** * Look up the best-matching handler method for the current request. * If multiple matches are found, the best match is selected. * @param lookupPath mapping lookup path within the current servlet mapping * @param request the current request * @return the best-matching handler method, or {@code null} if no match * @see #handleMatch(Object, String, HttpServletRequest) * @see #handleNoMatch(Set, String, HttpServletRequest) */ protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); //通过lookupPath,在this.urlLookup.get(urlPath)获取List<RequestMappingInfo>集合 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... 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) { 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 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
我们可以关注List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);这个方法实际上是从mappingRegistry中的urlLookup获得List集合. urlLookup的结构是Map<K, List>, K是url, List对应着RequestMappingInfo,实际上每一个RequestMapping最后都会被封装成RequestMappingInfo.
我们可以看到directPathMatches不为空,会调用addMatchingMappings(directPathMatches, matches, request),我们仔细关注T match = getMatchingMapping(mapping, 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))); } } }
当前请求的url和RequestMappingInfo中的PatternsRequestCondition对象中的url集合中是否匹配,如果匹配成功,返回一个新的RequestMappingInfo。
/** * Checks if all conditions in this request mapping info match the provided request and returns * a potentially new request mapping info with conditions tailored to the current request. * <p>For example the returned instance may contain the subset of URL patterns that match to * the current request, sorted with best matching patterns on top. * @return a new instance in case all conditions match; or {@code null} otherwise */ @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); if (methods == null || params == null || headers == null || consumes == null || produces == null) { return null; } PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { return null; } RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { return null; } return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
接着关注matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));这一行代码,Match是AbstractHandlerMethodMapping的内部类,this.mappingRegistry.getMappings()是获取映射注册中心的mappingLookup对象,其结构为Map<RequestMappingInfo, HandlerMethod>。
/** * A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of * comparing the best match with a comparator in the context of the current request. */ private class Match { private final T mapping; private final HandlerMethod handlerMethod; public Match(T mapping, HandlerMethod handlerMethod) { this.mapping = mapping; this.handlerMethod = handlerMethod; } @Override public String toString() { return this.mapping.toString(); } }
回到AbstractHandlerMethodMapping中的lookupHandlerMethod(String lookupPath, HttpServletRequest request)方法,如果matches为空,则遍历mappingRegistry中的mappingLookup集合,并且填充到matches。最后通过排序比较,获得matches集合中的第一个Match对象,此对象也是最匹配的,返回Match对象中的handlerMethod。
if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); }
回到AbstractHandlerMethodMapping中的getHandlerInternal(HttpServletRequest request)方法,如果获得的handlerMethod不为空,调用createWithResolvedBean()方法。其中的逻辑是如果当前handlerMethod中的bean只是bean的名称而不是真正的bean实例时,那么通过名称获得bean的实例。并且返回一个新的HandlerMethod。这里的bean是handlerMethd所属于的类。比如UserController中有一个login()方法,bean就是UserController,login就是HandlerMethod。最后释放mappingRegistry的读锁。
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
/** * If the provided instance contains a bean name rather than an object instance, * the bean name is resolved before a {@link HandlerMethod} is created and returned. */ public HandlerMethod createWithResolvedBean() { Object handler = this.bean; if (this.bean instanceof String) { String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } return new HandlerMethod(this, handler); }
回到AbstractHandlerMapping中的getHandler(HttpServletRequest request)方法,接着调用 getHandlerExecutionChain(handler, request),遍历所有的handlerInterceptor,把handler和handlerInterceptor(拦截器)封装成handlerExecutionChain(处理程序链)。还有一点就是MappedInterceptor里面有includePatterns和excludePatterns属性。通过这2个属性,设置需要被拦截的url和不需要被拦截的url。
/** * Build a {@link HandlerExecutionChain} for the given handler, including * applicable interceptors. * <p>The default implementation builds a standard {@link HandlerExecutionChain} * with the given handler, the handler mapping's common interceptors, and any * {@link MappedInterceptor}s matching to the current request URL. Interceptors * are added in the order they were registered. Subclasses may override this * in order to extend/rearrange the list of interceptors. * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a * pre-built {@link HandlerExecutionChain}. This method should handle those * two cases explicitly, either building a new {@link HandlerExecutionChain} * or extending the existing chain. * <p>For simply adding an interceptor in a custom subclass, consider calling * {@code super.getHandlerExecutionChain(handler, request)} and invoking * {@link HandlerExecutionChain#addInterceptor} on the returned chain object. * @param handler the resolved handler instance (never {@code null}) * @param request current HTTP request * @return the HandlerExecutionChain (never {@code null}) * @see #getAdaptedInterceptors() */ 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; }
返回到AbstractHandlerMapping中的getHandler(request)中,我们已经获取到executionChain对象,可以返回该对象。
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
返回到DispatcherServlet中的getHandler(HttpServletRequest request)返回当前request请求中的executionChain对象
HandlerExecutionChain handler = hm.getHandler(request);
我们继续回到DispatchServlet中的doDispatcher()方法,如果当前handlerExecutionChain(处理程序执行链)等于空或者handlerExecutionChain中的handlerMethod为空的话,就会抛出著名的NoHandlerFoundException异常
if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; }
如果你不信,可以点开noHandlerFound(processedRequest, response);
/** * No handler found -> set appropriate HTTP response status. * @param request current HTTP request * @param response current HTTP response * @throws Exception if preparing the response failed */ protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { if (pageNotFoundLogger.isWarnEnabled()) { pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) + "] in DispatcherServlet with name '" + getServletName() + "'"); } if (this.throwExceptionIfNoHandlerFound) { throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), new ServletServerHttpRequest(request).getHeaders()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); } }
接着看,我要从当前请求获取能够支持当前handlerMethod的适配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
点开代码看看,看看细节。主要是循环当前所有的handlerAdapters,通过supports()判断是否支持当前handlerMethod,这种循环比对思想在Spring MVC源码随处可见。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 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"); }
进入到AbstractHandlerMethodAdapter中的supports()方法,通过判断当前handler对象是否是HandlerMethod类的实例和是否支持当前handlerMethod。
/** * This implementation expects the handler to be an {@link HandlerMethod}. * @param handler the handler instance to check * @return whether or not this adapter can adapt the given handler */ @Override public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
点开,进入到RequestMappingHandlerAdapter中的supportsInternal(),恍然大悟。这个方法总是返回true,因为任何方法的参数和返回值都以某种方式处理
/** * Always return {@code true} since any method argument and return value * type will be processed in some way. A method argument not recognized * by any HandlerMethodArgumentResolver is interpreted as a request parameter * if it is a simple type, or as a model attribute otherwise. A return value * not recognized by any HandlerMethodReturnValueHandler will be interpreted * as a model attribute. */ @Override protected boolean supportsInternal(HandlerMethod handlerMethod) { return true; }
判断request的请求方式,如果是GET或者是HEAD,用户当前请求上一次请求的时间戳,通过checkNotModified()判断是否修改过。如果没有修改过,返回状态码304。
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; } }
如果handlerExecutionChain中的拦截器preHandle返回false,就不会调用postHandle(),直接清理资源,然后返回。
if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
进入HandlerExecutionChain中的applyPreHandle(processedRequest, response)方法。遍历HandlerExecutionChain中的所有拦截器,如果拦截器中的preHandle(request, response, this.handler)返回false,那么直接调用triggerAfterCompletion(request, response, null)进行资源清理,返回false。通过记录interceptorIndex来标志当前执行的拦截器。
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;
triggerAfterCompletion()方法,也是大同小异。遍历所有拦截器,调用拦截器中清理资源的方法afterCompletion()。
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); } } } }
小高潮来了,handlerAdapter(处理程序适配器)开始调用handlerMethod(处理程序)的功能方法。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
进入到AbstractHandlerMethodAdapter中的handle()方法
@Override public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }
进入RequestMappingHandlerAdapter中的handleInternal()方法,我们可以仔细看看这个方法做了什么。
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { //获取session,如果为空直接返回null HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
通过调用WebContentGenerator中的checkRequest()方法,判断支持的请求方式是否包含当前请求的方式,如果supportedMethods不为空且不支持当前请求方式,会抛出著名的HttpRequestMetohdNotSupportedException。如果需要session且从当前请求获得不到session,同样抛出HttpSessionRequiredException异常。
/** * Check the given request for supported methods and a required session, if any. * @param request current HTTP request * @throws ServletException if the request cannot be handled because a check failed * @since 4.2 */ protected final void checkRequest(HttpServletRequest request) throws ServletException { // Check whether we should support the request method. String method = request.getMethod(); if (this.supportedMethods != null && !this.supportedMethods.contains(method)) { throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods); } // Check whether a session is required. if (this.requireSession && request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } }
通过synchronizeOnSession标识符,判断调用invokeHandlerMethod是否需要同步机制。 然后调用invokeHandlerMethod()
mav = invokeHandlerMethod(request, response, handlerMethod);
进入到RequestMappingHandlerAdapter中的invokeHandlerMethod()中,首先RequestMappingHandlerAdapter支持{@link #setCustomArgumentResolvers}和{@link #setCustomReturnValueHandlers}配置自定义参数和自定义返回值,也支持来配置{@link #setArgumentResolvers}和{@link #setReturnValueHandlers}所有参数和返回值。
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); } invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
创建WebDataBinderFactory实例,用于创建WebDataBinder对象,用于web参数绑定。
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
比如我们现在有一个需求,从前台传来的日期字符串,我们要全部解析成Date类型的。一般有3种方式解决:PropertyEditor、Formatter、Converter去解决。最常见的做法实现WebBindingInitializer接口,通过WebDataBinder注册属性编辑器。
/** * WebBindingInitializer */ public class WebBindingInitializer implements org.springframework.web.bind.support.WebBindingInitializer { /* (non-Javadoc) * @see org.springframework.web.bind.support.WebBindingInitializer#initBinder(org.springframework.web.bind.WebDataBinder, org.springframework.web.context.request.WebRequest) */ @Override public void initBinder(WebDataBinder binder, WebRequest request) { binder.registerCustomEditor(String.class, new DatePropertyEditor()); } }
回到正轨,看一下是怎么创建WebDataBinderFactory实例。
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception { Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.initBinderCache.get(handlerType); if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); } List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>(); // Global methods first for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { initBinderMethods.add(createInitBinderMethod(bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); initBinderMethods.add(createInitBinderMethod(bean, method)); } return createDataBinderFactory(initBinderMethods); }
获得handlerMethod所在类的类型。通过所在类的类型获得从initBinderCache缓存中获得当前类所有的方法。这些方法应该是被@InitBinder注解的方法。
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<Class<?>, Set<Method>>(64);
如果methods等于空,那么我们去获得当前类下被@InitBinder注解的方法,并放入到initBinderCache缓存中。
if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); }
优先遍历被@ControllerAdvice注解全局类中的方法,再遍历被@Controller注解的类的方法。通过createInitBinderMethod(bean, method)方法创建InvocableHandlerMethod对象(用于参数准备,准备当中会用到WebDataBinderFactory创建WebDataBinder实例进行参数转换解析绑定,方法调用),并且放入到initBinderMethods集合中。
// Global methods first for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { initBinderMethods.add(createInitBinderMethod(bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); initBinderMethods.add(createInitBinderMethod(bean, method)); }
创建InvocableHandlerMethod对象,注入initBinderArgumentResolvers属性、parameterNameDiscoverer(属性名字发现器)、DefaultDataBinderFactory实例。我们发现要创建一个DefaultDataBinderFactory必须要传入webBindingInitializer。
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) { InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method); binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers); binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer)); binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); return binderMethod; }
最后调用createDataBinderFactory(initBinderMethods)方法创建ServletRequestDataBinderFactory实例,同样也要传入webBindingInitializer。ServletRequestDataBinderFactory是InitBinderDataBinderFactory的子类。
createDataBinderFactory(initBinderMethods); protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods) throws Exception { return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer()); }
接着创建出ModelFactory实例,我们首先要搞清楚ModelFactory是干啥的。ModelFactory作用是在控制器方法调用前初始化Model模型,调用后对Model模型进行更新。在初始化时,通过调用被@ModelAttribute注解的方法,Model模型会在会话中被临时存储的属性填充。
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
我们再来看是如何创建ModelFactory实例的,其实和创建WebDataBinderFactory的逻辑差不多。首先获取sessionAttributesHandler对象,同样这个对象是从sessionAttributesHandlerCache缓存获得到的。key是handlerMethod所在类的类型。如果sessionAttributesHandler没有从缓存中获取到,那么锁住缓存,再从缓存中取一遍。如果sessionAttributesHandler还为空的话,那么自己通过new SessionAttributesHandler(handlerType, sessionAttributeStore)创建一个默认的sessionAttributesHandler对象,并放入到缓存中。这种思想是享元设计模式。
/** * Return the {@link SessionAttributesHandler} instance for the given handler type * (never {@code null}). */ private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) { Class<?> handlerType = handlerMethod.getBeanType(); SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { synchronized (this.sessionAttributesHandlerCache) { sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); if (sessionAttrHandler == null) { sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore); this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler); } } } return sessionAttrHandler; }
创建SessionAttributesHandler过程
/** * Create a new instance for a controller type. Session attribute names and * types are extracted from the {@code @SessionAttributes} annotation, if * present, on the given type. * @param handlerType the controller type * @param sessionAttributeStore used for session access */ public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) { Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null"); this.sessionAttributeStore = sessionAttributeStore; SessionAttributes annotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class); if (annotation != null) { this.attributeNames.addAll(Arrays.asList(annotation.names())); this.attributeTypes.addAll(Arrays.asList(annotation.types())); } this.knownAttributeNames.addAll(this.attributeNames); }
接着通过modelAttributeCache中获取handlerMethod所在类中所有被@ModelAttribute注解且没有被@RequestMapping注解的方法。如果没有从缓存中查找到,那么通过 MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)查找,并加入到modelAttributeCache缓存中。
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.modelAttributeCache.get(handlerType); if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS); this.modelAttributeCache.put(handlerType, methods); } List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>(); // Global methods first for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); }
接着老操作,通过createModelAttributeMethod(binderFactory, bean, method)方法创建InvocableHandlerMethod对象,并放入到attrMethods集合中。
private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) { InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method); attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); attrMethod.setDataBinderFactory(factory); return attrMethod; }
终于到了new ModelFactory()这一步。通过handlerMethods集合、WebDataBinderFactory实例,SessionAttributesHandler实例创建出ModelFactory实例。
public ModelFactory(List<InvocableHandlerMethod> handlerMethods, WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) { if (handlerMethods != null) { for (InvocableHandlerMethod handlerMethod : handlerMethods) { this.modelMethods.add(new ModelMethod(handlerMethod)); } } this.dataBinderFactory = binderFactory; this.sessionAttributesHandler = attributeHandler; }
既然binderFactory、modelFactory都被我们造出来了,那肯定要干正紧事情了。对handlerMethod进行下一步包装,填充argumentResolvers(HandlerMethodArgumentResolverComposite)、returnValueHandlers(HandlerMethodReturnValueHandlerComposite)、binderFactory、parameterNameDiscoverer属性包装成ServletInvocableHandlerMethod。ServletInvocableHandlerMethod的作用对处理程序的返回值进行处理和ResponseStatus处理。
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
实例化ModelAndViewContainer容器,把request里面的属性名为"org.springframework.web.servlet.DispatcherServlet.INPUT_FLASH_MAP"的重定向参数注入到容器中的model模型中。FlashMap的作用是在redirect中传递参数。重定向是会生成新的request,那么传递参数就不能直接用request进行传递。
ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
我们关注到initModel(webRequest, mavCOntainer, invocableMethod)这一行,它到底干了什么事情。首先从request中获取检索@SessionAttribute中名称的属性,以Map<String, Object>的结构存储起来,并且放入到ModelAndViewContainer容器的model中。
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception { Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); container.mergeAttributes(sessionAttributes); invokeModelAttributeMethods(request, container); for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } } }
接着调用invokeModelAttributeMethods(request, container)方法,将被@ModelAttribute注解的handlerMethod中的模型填充到ModelAndViewContainer容器中的model。只有当容器中不包含当前@ModelAtrribute中的属性时才添加该属性至容器。同时还要判断当前@ModelAttribute中的属性能不能添加到容器中,如果不能,那么放到容器中的bindingDisabledAttributes进行标记。然后提前调用被@ModelAttribute注解的handlerMethod,只有handlerMethod的返回值类型是void,才能将进行数据绑定(也就是绑定到容器中的model里)。如果handlerMethod的返回类不是void,那太好了可以进行数据绑定。数据绑定的规则是如果@ModelAttribute注解设置value和name属性了,优先选择value和name属性作为model中的key,如果没有设置value和name属性,选择被@ModelAttribute注解的handlerMethod的返回类型名称(首字母小写)作为model中的key。
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception { while (!this.modelMethods.isEmpty()) { InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod(); ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class); if (container.containsAttribute(ann.name())) { if (!ann.binding()) { container.setBindingDisabled(ann.name()); } continue; } Object returnValue = modelMethod.invokeForRequest(request, container); if (!modelMethod.isVoid()){ String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType()); if (!ann.binding()) { container.setBindingDisabled(returnValueName); } if (!container.containsAttribute(returnValueName)) { container.addAttribute(returnValueName, returnValue); } } } }
说的通俗点,如果被@ModelAttribute注解的handlerMethod返回类型是Collection或者是数组类型,那么填充到model中的key就是方法返回类型名称(首字母小写)再拼接上List。看下面例子,key就是stringList
@ModelAttribute public List<String> baseTest1() { List<String> list = new ArrayList<>(); list.add("1"); return list; } @ModelAttribute public String[] baseTest2() { String[] strings = new String[1]; strings[0] = "1"; return strings; }
如果返回类型是String或者是Map,那么key就是string、map
@ModelAttribute public String baseTest() { return "1"; } @ModelAttribute public Map<String, String> baseTest3() { Map<String, String> map = new HashMap<>(); map.put("username", "password"); return map; }
为什么会是这样呢,可以看到ModelFactory中的getNameForReturnType(),首先判断@ModelAttribute注解value属性是不是为空。如果不为空,取value属性的值。如果为空,进行Conventions.getVariableNameForReturnType(method, resolvedType, returnValue)操作。
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class); if (ann != null && StringUtils.hasText(ann.value())) { return ann.value(); } else { Method method = returnType.getMethod(); Class<?> containingClass = returnType.getContainingClass(); Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass); return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue); } }
看到getVariableNameForReturnType(),心中的疑惑应该解开了把。如果返回类型是Object类型,我们会通过返回值来得出它实际返回类型,再通过实际返回类型推出所在类的简称,再进行格式化返回其短名称(也就是首字母小写,也可以说小驼峰)。如果返回类型是Array类型或者是Collection类型,就在其返回基础上再拼接"List"字符串。
public static String getVariableNameForReturnType(Method method, Class<?> resolvedType, Object value) { Assert.notNull(method, "Method must not be null"); if (Object.class == resolvedType) { if (value == null) { throw new IllegalArgumentException("Cannot generate variable name for an Object return type with null value"); } return getVariableName(value); } Class<?> valueClass; boolean pluralize = false; if (resolvedType.isArray()) { valueClass = resolvedType.getComponentType(); pluralize = true; } else if (Collection.class.isAssignableFrom(resolvedType)) { valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric(); if (valueClass == null) { if (!(value instanceof Collection)) { throw new IllegalArgumentException( "Cannot generate variable name for non-typed Collection return type and a non-Collection value"); } Collection<?> collection = (Collection<?>) value; if (collection.isEmpty()) { throw new IllegalArgumentException( "Cannot generate variable name for non-typed Collection return type and an empty Collection value"); } Object valueToCheck = peekAhead(collection); valueClass = getClassForValue(valueToCheck); } pluralize = true; } else { valueClass = resolvedType; } String name = ClassUtils.getShortNameAsProperty(valueClass); return (pluralize ? pluralize(name) : name); } private static final String PLURAL_SUFFIX = "List"; private static String pluralize(String name) { return name + PLURAL_SUFFIX; }
回到ModelFactory中的initModel()上,把目光集中到下面这行代码上。
for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } }
遍历HandlerMethod的参数,判断参数是否被@ModelAttribute注解,如果有,继续判断这个参数和参数类型是否和当前handlerMethod所在类中的@SessionAttributes注解中的参数和类型是否保持一致。
/** * Find {@code @ModelAttribute} arguments also listed as {@code @SessionAttributes}. */ private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) { List<String> result = new ArrayList<String>(); for (MethodParameter parameter : handlerMethod.getMethodParameters()) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { String name = getNameForParameter(parameter); Class<?> paramType = parameter.getParameterType(); if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) { result.add(name); } } } return result; }
/** * Whether the attribute name or type match the names and types specified * via {@code @SessionAttributes} on the underlying controller. * <p>Attributes successfully resolved through this method are "remembered" * and subsequently used in {@link #retrieveAttributes(WebRequest)} and * {@link #cleanupAttributes(WebRequest)}. * @param attributeName the attribute name to check * @param attributeType the type for the attribute */ public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) { Assert.notNull(attributeName, "Attribute name must not be null"); if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) { this.knownAttributeNames.add(attributeName); return true; } else { return false; } }
通过findSessionAttributeArguments(handlerMethod)方法,我们得到了合适的参数名称集合。遍历这个集合,我们要判断ModelAndViewContainer容器中是否存在相同名称的参数。如果不存在,我们从sessionAttributeStore根据名称中获得这个参数的值,最后将参数绑定到容器中。
for (String name : findSessionAttributeArguments(handlerMethod)) { if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name); } container.addAttribute(name, value); } }
设置ModelAndViewContainer容器使用defaultModel(默认模型),而不是redirectModel(重定向模型)。
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); //ModelAndViewContainer类中的方法 public ModelMap getModel() { if (useDefaultModel()) { return this.defaultModel; } else { if (this.redirectModel == null) { this.redirectModel = new ModelMap(); } return this.redirectModel; } } private boolean useDefaultModel() { return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect)); }
处理一些异步请求。
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } invocableMethod = invocableMethod.wrapConcurrentResult(result); }
接着invocableMethod去调用invokeAndHandle这个方法。invokeAndHandle是ServletInvocableHandlerMethod中的方法。
invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest);
ServletInvocableMethod中的invokeAndHandle()其实是间接调用handlerMethod,然后处理handlerMethod的返回值。
/** * Invoke the method and handle the return value through one of the * configured {@link HandlerMethodReturnValueHandler}s. * @param webRequest the current request * @param mavContainer the ModelAndViewContainer for this request * @param providedArgs "given" arguments matched by type (not resolved) */ public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } }
我们可以看到invokeForRequest(webRequest, mavContainer, providedArgs)这个方法会返回handlerMethod的返回值。这个方法在给定请求的上下文中解析handlerMethod的方法参数后,然后去调用handlerMethod。参数的解析是通过 {@link HandlerMethodArgumentResolver}完成的。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "' with arguments " + Arrays.toString(args)); } Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; }
InvocableHandlerMethod中的getMethodArgumentValues()这个方法是获取handlerMethod的参数。首先获取handlerMethod中的所有参数数组,数组类型是MethodParameter。遍历参数数组,给每一个参数初始化parameterNameDisconverer(参数名称发现器)。
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (this.argumentResolvers.supportsParameter(parameter)) { try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } if (args[i] == null) { throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() + ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; }
看到resolveProvidedArgument(parameter, providedArgs)这行代码,里面会对提供的providedArgs参数进行类型判断,判断它是否和MethodParameter类型匹配。如果类型匹配,返回提供的参数。如果不匹配,返回null。
args[i] = resolveProvidedArgument(parameter, providedArgs); /** * Attempt to resolve a method parameter from the list of provided argument values. */ private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) { if (providedArgs == null) { return null; } for (Object providedArg : providedArgs) { if (parameter.getParameterType().isInstance(providedArg)) { return providedArg; } } return null; }
接着看,如果提供的参数值不为空,那么跳出当前循环,继续下一次循环。
args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; }
我们通过supportParameter()方法判断argumentResolvers(这是HandlerMethodArgumentResovlerComposite对象,参数解析器处理链)是否支持parameter这种类型的参数解析。
if (this.argumentResolvers.supportsParameter(parameter)) { try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } }
HandlerMethodArgumentResovlerComposite中的supportsParamter()方法,通过parameter参数类型去获得合适的HandlerMethodArgumentResolver(参数解析器)。如果没有合适的参数解析器,那么就说明HandlerMethodArgumentResolverComposite中没有合适的参数解析器能解析这种类型的参数。
/** * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered * {@link HandlerMethodArgumentResolver}. */ @Override public boolean supportsParameter(MethodParameter parameter) { return (getArgumentResolver(parameter) != null); }
首先通过parameter参数类型从argumentResolverCache缓存中获得合适的参数解析器。 如果没有找到,那么遍历HandlerMethodArgumentResolverComposite中所有的参数器,直到找到能够解析该parameter类型的参数解析器为止,且放入到argumentResolverCache缓存中,缓存的初始容量是256。
/** * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter. */ private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (logger.isTraceEnabled()) { logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]"); } if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }
如果最后的参数还为空的话,那么很遗憾抛IllegalStateException异常,没有合适的参数解析器能够解析这个参数。
if (args[i] == null) { throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() + ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i)); }
到了最为关键的一步,开始解析参数。
try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; }
进入到HandlerMethodArgumentResolverComposite中的resolveArgument()放到,老样子从缓存中获得合适的参数解析器。并且由这个参数解析器来解析这个参数。
/** * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it. * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found. */ @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]"); } return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }
HandlerMethod所需的方法参数都已经解析完毕,那么就可以开始调用HandlerMethod了。回到InvocableHandlerMethod中的invokeForRequest()方法。
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "' with arguments " + Arrays.toString(args)); } Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; }
调用doInvoke(args)方法,我们可以看到通过ReflectionUtils.makeAccessible(getBridgedMethod())让handlerMethod方法具有访问性,必要要显式的设置它具有访问性。然后准备好handlerMethod所在类的实例和方法参数,反射调用handlerMethod。
/** * Invoke the handler method with the given argument values. */ protected Object doInvoke(Object... args) throws Exception { ReflectionUtils.makeAccessible(getBridgedMethod()); try { return getBridgedMethod().invoke(getBean(), args); } catch (IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); throw new IllegalStateException(getInvocationErrorMessage(text, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... Throwable targetException = ex.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException) targetException; } else if (targetException instanceof Error) { throw (Error) targetException; } else if (targetException instanceof Exception) { throw (Exception) targetException; } else { String text = getInvocationErrorMessage("Failed to invoke handler method", args); throw new IllegalStateException(text, targetException); } } }
handlerMethod调用完毕后,可以要对返回值进行处理的操作。这时候可以关注ServletInvocableHandlerMethod中的invokeAndHandle()方法。首先是设置ResponseStatus的状态,如果有用到{@link ResponseStatus}注解来设置响应状态。mavContainer.setRequestHandled(false)只是初始化时默认采用view的解决方案,设置为true表示response直接处理,不需要view的解决方案。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } }
实际上handlerMethod的返回值处理是通过HandlerMethodReturnValueHandlerComposite的handleReturnValue()方法,还是老样子,遍历所有的返回值处理器,通过supportsReturnType()判断是否支持该返回值的类型。如果类型支持的话,那么就让合适的HandlerMethodReturnValueHandler去处理handlerMethod的返回值。这里的returnType其实是ReturnValueMethodParameter类型的。(有兴趣,可以看HandlerMethod这个类)
// 调用HandlerMethodReturnValueHandlerComposite的handleReturnValue()方法 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); /** * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it. * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found. */ @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null; }
回到RequestMappingHandlerAdapter中的invokeHandlerMethod()方法中
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ... invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
首先调用modelFactory.updateModel(webRequest, mavContainer)将被@SessionAtrributes注解的模型属性上升到会话级别。如果mavContainer采用的是response直接处理策略,说明没有采用view的解决方案,直接返回null即可。将ModelAndViewContainer中的model、视图的名称、HttpStatus填充到ModelAndView中。如果mavContainer没有指定逻辑视图(或者说 view不是String类型的)的话,那么就设置视图对象。如果model是RedirectAttributes的实例,那么说明是model是重定向所需要的属性,我们把model填充到FlashMap即可。
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; }
这里的updateModel()首先获得defaultModel,然后判断当前会话是否处理完毕。如果处理完毕,进行资源清理操作。如果没有处理完毕,把当前request中的model对象保存在SessionAttributesHandler中的sessionAttributeStore中,方便下次请求。如果container采用的是view策略并且使用的是默认model模型,那么就调用updateBindingResult(request, defaultModel)方法,为需要它的属性添加到BindingResult属性到defaultModel中。
/** * Promote model attributes listed as {@code @SessionAttributes} to the session. * Add {@link BindingResult} attributes where necessary. * @param request the current request * @param container contains the model to update * @throws Exception if creating BindingResult attributes fails */ public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { ModelMap defaultModel = container.getDefaultModel(); if (container.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!container.isRequestHandled() && container.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } }
回到RequestMappingHandlerAdapter中的hanlderInternal()方法中,如果response的响应条没有设置Cache-control属性的话,如果handlerMethod对应的SessionAttributesHandler中维护了被@SessionAtrribute注解的model,那么设置Cache-control为no store模式。否则设置Cache-control为-1。
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); ... // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { // 调用的是WebContentGeneratorl类中的方法 applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
回到DispatcherServlet中的doDispatch()方法,我们通过适配器调用HandlerExecutionChain中的handler返回ModelAndView,如果ModelAndView中没有视图引用,那么申请设置默认的视图名称。然后调用HandlerExecutionChain中所有的拦截器中的postHandle()方法,对handlerMethod返回的结果进行加强处理。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... // 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; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", 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); } } } }
接着调用processDispatchResult()方法,开始对处理程序调用返回的结构进行处理。要么是ModelAndView,要么解析成ModelAndView的异常。如果异常不为空且是ModelAndViewDefiningException类型的异常,那么把视图解析成ModelAndViewDefiningException特定的视图。如果异常不为空且不是ModelAndViewDefiningException类型的异常,那么调用 processHandlerException()让HandlerExceptionResovlers中的异常处理器来处理。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); /** * Handle the result of handler selection and handler invocation, which is * either a ModelAndView or an Exception to be resolved to a ModelAndView. */ private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
processHandlerException()内部实现是遍历DispatcherServlet中的handlerExceptionResolvers(程序异常解析器集合),如果当前有异常解析器能够处理这个异常且处理完毕后返回的ModelAndView不为空,那么跳出循环。然后继续判断,如果ModelAndView中的model属性并且view属性都为空的话,把异常信息放到request中(EXCEPTION_ATTRIBUTE的值是 DispatcherServlet.class.getName() + ".EXCEPTION"),直接返回null。
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Check registered HandlerExceptionResolvers... ModelAndView exMv = null; for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) { exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } if (exMv != null) { if (exMv.isEmpty()) { request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } // We might still need view name translation for a plain error model... if (!exMv.hasView()) { exMv.setViewName(getDefaultViewName(request)); } if (logger.isDebugEnabled()) { logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
同时我们也可以看看HandlerExceptionResolverComposite中的resolveException()方法。通过遍历已配置的异常解析器列表来解决处理异常,如果处理返回的ModelAndView实例不为空,那么直接返回ModelAndView实例。
@Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) { if (this.resolvers != null) { for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) { ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex); if (mav != null) { return mav; } } } return null; }
回到DispatcherServlet中的processDispatchResult(),如果ModelAndView实例不为空且modelAndView中的model和view属性不为空,那么进行render()操作。render()操作之后,判断errorView是否为true,如果为true,代表已经有错误视图去响应错误,那么就可以清理request中一些关于错误的属性(status_code、exception_type、message、exception、request_uri、servlet_name)。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; .... // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
//WebUtils /** * Standard Servlet 2.3+ spec request attributes for error pages. * <p>To be exposed to JSPs that are marked as error pages, when forwarding * to them directly rather than through the servlet container's error page * resolution mechanism. */ public static final String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code"; public static final String ERROR_EXCEPTION_TYPE_ATTRIBUTE = "javax.servlet.error.exception_type"; public static final String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message"; public static final String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception"; public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri"; public static final String ERROR_SERVLET_NAME_ATTRIBUTE = "javax.servlet.error.servlet_name"; /** * Clear the Servlet spec's error attributes as {@link javax.servlet.http.HttpServletRequest} * attributes under the keys defined in the Servlet 2.3 specification: * {@code javax.servlet.error.status_code}, * {@code javax.servlet.error.exception_type}, * {@code javax.servlet.error.message}, * {@code javax.servlet.error.exception}, * {@code javax.servlet.error.request_uri}, * {@code javax.servlet.error.servlet_name}. * @param request current servlet request */ public static void clearErrorRequestAttributes(HttpServletRequest request) { request.removeAttribute(ERROR_STATUS_CODE_ATTRIBUTE); request.removeAttribute(ERROR_EXCEPTION_TYPE_ATTRIBUTE); request.removeAttribute(ERROR_MESSAGE_ATTRIBUTE); request.removeAttribute(ERROR_EXCEPTION_ATTRIBUTE); request.removeAttribute(ERROR_REQUEST_URI_ATTRIBUTE); request.removeAttribute(ERROR_SERVLET_NAME_ATTRIBUTE); }
看看render()里面是干嘛的,它实际上是为了呈现ModelAndView,这是处理请求的最后一步,里面涉及到将逻辑视图转换成真正的物理视图。
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. Locale locale = this.localeResolver.resolveLocale(request); response.setLocale(locale); View view; if (mv.isReference()) { // We need to resolve the view name. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // No need to lookup: the ModelAndView object contains the actual View object. view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } // Delegate to the View object for rendering. if (logger.isDebugEnabled()) { logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'"); } try { if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'", ex); } throw ex; } }
resolveViewName()其实将视图名称转换成真正的视图对象。通过遍历当前所有的viewResolvers,如果视图解析器解析后的view对象不为空的话,那么直接返回当前view对象。
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; }
进入到ContentNegotiatingViewResolver。ContentNegotiatingViewResolver根据请求头中的Accept属性或者是请求文件名来解析视图。它本身不提供解析视图,而是通过viewResolvers集合中的视图解析器来解析视图。resolveViewName()这个方法,通过请求头中的Accept属性获取requestMediaTypes,由此获取与之兼容的view对象集合。再通过兼容的view对象集合获得最佳匹配的view对象(AbstractMessageConverterMethodProcessor也用了类似的方法,获得最佳的selectedMediaType)。
@Override public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes"); List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); if (requestedMediaTypes != null) { List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); View bestView = getBestView(candidateViews, requestedMediaTypes, attrs); if (bestView != null) { return bestView; } } if (this.useNotAcceptableStatusCode) { if (logger.isDebugEnabled()) { logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code"); } return NOT_ACCEPTABLE_VIEW; } else { logger.debug("No acceptable view found; returning null"); return null; } }
获取到合适的view对象,那么调用其本身的render()方法,将model对象填充到到view对象中,完成渲染操作。
if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } view.render(mv.getModelInternal(), request, response);
对于一个框架,我们不仅要做到熟练使用,还用知其然知其所以然。以后我再写关于框架源码分析的文章,会尽量切割,缩小篇幅。