閱讀本文章大概須要8分鐘左右。相信會讓你對Spring MVC的理解更加深入,更上一層樓。前端
粒度很粗的圖解 java
![]()
粒度比較粗的圖解ios
![]()
粒度通常的圖解web
![]()
FrameworkServlet是Spring MVC框架中的基本Servlet,集成提供了Spring應用的上下文。經過讀取咱們在web.xml中配置的ContextConfigLocation、ContextLoaderListener、ContextClass屬性注入上下文。子類必須重寫doService()方法去處理請求。spring
假如咱們要請求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對象的參數進行快照,以便在包含後恢復這些屬性。這些屬性分別是bash
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()方法session
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}全部參數和返回值。
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
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);
複製代碼
對於一個框架,咱們不只要作到熟練使用,還用知其然知其因此然。之後我再寫關於框架源碼分析的文章,會盡可能切割,縮小篇幅。