接收到一個(gè)新的請(qǐng)求之后贬媒,spring就會(huì)去根據(jù)請(qǐng)求的URL信息選擇具體的代碼塊去執(zhí)行操作不铆,如圖就是接收到一個(gè)新的請(qǐng)求調(diào)用圖四敞,從Tomcat開始直到把請(qǐng)求分發(fā)到spring中桐玻,最后到了doDispatch方法
本篇學(xué)習(xí)筆記主要就是講一個(gè)新的請(qǐng)求被分發(fā)到spring中spring如何處理迟螺,至于如何掃描包中的controller冲秽,得到URL配置信息可以看Spring MVC URL映射 學(xué)習(xí)(上),而本篇主要介紹了
- 獲取執(zhí)行鏈
- 404頁(yè)面設(shè)置
- 獲取適配器
- LastModified
- 內(nèi)容方法調(diào)用(invoke)
- 視圖渲染
讓我們更加清楚的知道spring中一般的方法是如何確定調(diào)用的具體方法的,適配不同的模板引擎矩父,達(dá)到渲染的地步锉桑,再者有時(shí)候又是API一般只需要返回json結(jié)構(gòu)的數(shù)據(jù),其背后的原理是如何實(shí)現(xiàn)的窍株,以及我們?cè)谑褂眠^程中如何避免出現(xiàn)的各種問題民轴。
doDispatch 方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
// 初始化設(shè)置模板為null
Exception dispatchException = null;
// 初始化異常為null
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 文件上傳的相關(guān)設(shè)置和操作
mappedHandler = getHandler(processedRequest);
// 根據(jù)request獲取對(duì)應(yīng)的請(qǐng)求執(zhí)行鏈
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// 如果沒有對(duì)應(yīng)的handler對(duì)于,則應(yīng)該是定義為404球订,通過noHandlerFound確認(rèn)
noHandlerFound(processedRequest, response);
return;
}
// 通過handler獲得合適的handler適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
// 是get方法或者h(yuǎn)ead方法
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
// 如果handler 是LastModified類后裸,則獲取其lastModified值,否則返回-1
// lastModify 是spring添加了緩存機(jī)制冒滩,當(dāng)重復(fù)請(qǐng)求同樣的內(nèi)容轻抱,返回403,而不會(huì)返回真正的內(nèi)容旦部,具體可看下面的LastModified機(jī)制這一小節(jié)
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置的handler預(yù)處理祈搜,就是獲取執(zhí)行鏈的攔截器较店,對(duì)請(qǐng)求進(jìn)行攔截處理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 如果攔截器攔截成功,返回false容燕,直接結(jié)束了
// 當(dāng)然在這其中攔截器肯定需要特定返回自身的內(nèi)容到response中梁呈,便于展示在頁(yè)面上
// 不過從頁(yè)面角度出發(fā)并沒有非常實(shí)質(zhì)性的攔截器處理,這點(diǎn)存疑蘸秘?
return;
}
// 真正的調(diào)用各自的執(zhí)行方法官卡,返回ModelAndView后續(xù)在invoke這一小節(jié)細(xì)說
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 如果mv沒有包含有效的視圖,則從dispatch的viewNameTranslator屬性上獲取對(duì)應(yīng)的默認(rèn)視圖
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 攔截器的后置處理
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 到這里了可能存在異常醋虏,類似的500請(qǐng)求處理就是在這里完成操作的
// 同時(shí)包含了數(shù)據(jù)回填到response中的操作
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);
}
}
}
}
獲取執(zhí)行鏈
AbstractHandlerMapping 類
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
// 這個(gè)handlerMappings就是在掃描URL得到的URL容器信息
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
如圖所示就是
getHandler 方法
AbstractHandlerMapping 類
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
// 調(diào)用子類實(shí)現(xiàn)的具體方法寻咒,獲得執(zhí)行鏈對(duì)象
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
// 到這里意味著真的找不到可用的執(zhí)行鏈對(duì)象
return null;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 重新生成一個(gè)執(zhí)行鏈對(duì)象,可能會(huì)和handler是同一個(gè)對(duì)象
if (CorsUtils.isCorsRequest(request)) {
// 查看request頭部信息是否包含了Origin信息颈嚼,如果有則判斷成功
// cors跨域設(shè)置
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
// 獲取全局的跨域配置
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
// 結(jié)合當(dāng)前handler和全局配置組合成一個(gè)新的跨域配置
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
// 重新生成可用的執(zhí)行鏈對(duì)象
}
return executionChain;
}
AbstractUrlHandlerMapping類
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 從request中獲取到URL信息
Object handler = lookupHandler(lookupPath, request);
// 得到執(zhí)行鏈路的對(duì)象毛秘,一般為HandlerExecutionChain
if (handler == null) {
// 如果沒有找到,則匹配一些特殊的請(qǐng)求阻课,盡可能的匹配清楚
Object rawHandler = null;
if ("/".equals(lookupPath)) {
// 根路徑的handler
rawHandler = getRootHandler();
}
if (rawHandler == null) {
// 默認(rèn)路徑的handler
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// 根路徑或者默認(rèn)路徑中的一個(gè)不為null
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
// 同樣的套路得到新的執(zhí)行鏈路對(duì)象信息
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
// 打個(gè)日志叫挟,表示匹配到了相關(guān)的bean信息
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 從hanlerMap中獲取該URL對(duì)應(yīng)的controller
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
//
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 沒有完全匹配,采取模糊匹配限煞,正則還是抹恳?
// 題外話,python的tornado和Django都是采用的正則匹配的
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
// getPathMatcher() 返回的就是AntPathMatcher對(duì)象
// 同樣也是按照getPathMatcher的匹配規(guī)則去匹配的
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}
// 總之最后會(huì)得到一個(gè)list署驻,包含了匹配中的URL信息
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
// 模糊匹配的集合不為空,則按照urlPath定義的排序規(guī)則去重排序
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
// 第一個(gè)是最佳的匹配路徑
}
if (bestMatch != null) {
// 接下來的一切都是依照這個(gè)bestMatch不為null奋献,否則就直接返回null,表示沒有合適的匹配handler
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
// 如果容器中不存在該handler
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
// 對(duì)最佳的URL路徑處理之后旺上,還是不在對(duì)應(yīng)的handler秽荞,則提示沒有具體的handler信息
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// 包含了對(duì)應(yīng)的handler信息了
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
// 如果是string,則通過spring的容器獲取對(duì)應(yīng)的bean(一般是controller)
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
//
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
// 生成執(zhí)行鏈路對(duì)象HandlerExecutionChain
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// 什么都沒有發(fā)現(xiàn)抚官,返回null
return null;
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
// 創(chuàng)建執(zhí)行鏈路的對(duì)象chain
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
// 添加PathExposingHandlerInterceptor攔截器
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
如圖就是調(diào)用buildPathExposingHandler之后返回的執(zhí)行鏈對(duì)象chain
404狀態(tài)碼
在沒找到合適的handler執(zhí)行鏈的時(shí)候扬跋,就會(huì)進(jìn)入該方法(其實(shí)就是404了)
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
// 類似這種代碼其實(shí)是為了適配不同的日志系統(tǒng)
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 {
// HttpServletResponse.SC_NOT_FOUND 就是404
// 調(diào)用的是Tomcat本身的404設(shè)置頁(yè)面返回的,可以在web.xml 中配置自定義的404頁(yè)面
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
如圖就是采用了jdk14Logger打印出來的數(shù)據(jù)凌节,當(dāng)然了現(xiàn)實(shí)項(xiàng)目中更多的應(yīng)用log4j钦听、log4j2
獲取適配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
// handlerAdapters的值就是在initStrategies()方法中獲取到的
// 默認(rèn)的適配器是三個(gè)
// org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
// org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
// org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
// 如果細(xì)看適配器的初始化過程會(huì)發(fā)現(xiàn),spring會(huì)優(yōu)先獲取用戶自定義的適配器倍奢,如果沒有才會(huì)默認(rèn)使用這三個(gè)適配器
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");
// 常出現(xiàn)的沒有找到適配器的報(bào)錯(cuò)的地方就是這里朴上,遍歷了所有的適配器并沒有符合handler的適配器,則提示沒有適配器
}
接下來看看三種適配器到底是如何判斷對(duì)象是符合自身的要求的
HttpRequestHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
// 是否是HttpRequestHandler對(duì)象
}
SimpleControllerHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
// 是否是Controller對(duì)象
}
AnnotationMethodHandlerAdapter
@Override
public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
// 獲取到方法處理器卒煞,再判斷其是否為空
}
private ServletHandlerMethodResolver getMethodResolver(Object handler) {
Class<?> handlerClass = ClassUtils.getUserClass(handler);
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
if (resolver == null) {
synchronized (this.methodResolverCache) {
resolver = this.methodResolverCache.get(handlerClass);
if (resolver == null) {
resolver = new ServletHandlerMethodResolver(handlerClass);
this.methodResolverCache.put(handlerClass, resolver);
}
}
}
return resolver;
}
LastModified 機(jī)制
組合成為一個(gè)ServletWebRequest對(duì)象后就調(diào)用該方法痪宰,確認(rèn)lastModified
public boolean checkNotModified(String etag, long lastModifiedTimestamp) {
HttpServletResponse response = getResponse();
if (this.notModified || !isStatusOK(response)) {
// 不需要modified機(jī)制或者 response為null或者無效更或者狀態(tài)碼不是200
return this.notModified;
}
if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
// 其對(duì)象會(huì)存儲(chǔ)該請(qǐng)求的時(shí)間,驗(yàn)證是否符合lastModified的條件
if (this.notModified) {
response.setStatus(HttpStatus.PRECONDITION_FAILED.value());
}
return this.notModified;
}
boolean validated = validateIfNoneMatch(etag);
// 默認(rèn)傳過來的etag是null,返回false
if (!validated) {
validateIfModifiedSince(lastModifiedTimestamp);
// 看是否是第一次來衣撬,如果是就需要刷新其時(shí)間
}
boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
// 是否為get或者h(yuǎn)ead請(qǐng)求乖订,并且變更response的頭信息
if (this.notModified) {
response.setStatus(isHttpGetOrHead ?
HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
}
if (isHttpGetOrHead) {
if(lastModifiedTimestamp > 0 && isHeaderAbsent(response, LAST_MODIFIED)) {
response.setDateHeader(LAST_MODIFIED, lastModifiedTimestamp);
}
if (StringUtils.hasLength(etag) && isHeaderAbsent(response, ETAG)) {
response.setHeader(ETAG, padEtagIfNecessary(etag));
}
}
// 如果符合其機(jī)制,就返回true具练,否則返回false
return this.notModified;
}
invoke調(diào)用
只是取了這么個(gè)名字乍构,在獲取到了合適的執(zhí)行鏈,有了合適的適配器扛点,有沒有被攔截器處理哥遮,最后來到了真正調(diào)用方法的地方了。
上文可知陵究,這是由各自的適配器調(diào)用handler方法來獲取真正的內(nèi)容了眠饮。
在沒看源碼前,其實(shí)我們應(yīng)該能夠猜到一些細(xì)節(jié)铜邮,例如
- 上面說的bean的name是\開頭的被SimpleControllerHandlerAdapter命中仪召,可是在該方法中并沒有明確定義執(zhí)行的方法,所以controller接口必須有一些方法要被實(shí)現(xiàn)牲距,然后由適配器調(diào)用實(shí)現(xiàn)類達(dá)到獲取內(nèi)容的目的
- 普通的加了requestMapping被AnnotationMethodHandlerAdapter命中返咱,則應(yīng)該是通過反射獲取所有的可行的方法钥庇,然后依次篩選牍鞠,直到匹配上了合適的方法,再invoke調(diào)用返回其內(nèi)容
SimpleControllerHandlerAdapter
HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter是一樣的评姨,就只解釋一種了难述。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
// 果然不出我們期望的,就是controller接口實(shí)現(xiàn)的方法handleRequest去完成真正的調(diào)用
// 如下面的demo吐句,就是調(diào)用該方法胁后,不過此方法意味著一個(gè)類只有一個(gè)URL映射
// 不像注解那樣一個(gè)類中可以包含多種URL映射
}
public class NameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("/page/404");
return modelAndView;
}
}
AnnotationMethodHandlerAdapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Class<?> clazz = ClassUtils.getUserClass(handler);
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
checkAndPrepare(request, response, true);
}
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
// 前面各種操作來到了inVokeMethod
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
// 處理request,得到對(duì)應(yīng)的需要被執(zhí)行的方法
// 在這個(gè)函數(shù)中還有類URL注解信息和方法注解信息的拼接匹配過程
// 返回的對(duì)象就是一個(gè)Method對(duì)象
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
// 這個(gè)是真正的調(diào)用過程嗦枢,其中還包含了請(qǐng)求參數(shù)的處理等各種過程
// 返回的結(jié)果可以是字符串也可以是mv,看具體的方法是如何實(shí)現(xiàn)的
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
// 獲取具體的視圖類,如果是具體業(yè)務(wù)返回步咪,視圖為null坷备,具體的數(shù)據(jù)已經(jīng)通過response的body回填返回了
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
// 視圖屬性更新
return mav;
}
public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(handlerMethod, ResponseStatus.class);
// 查看該方法是否加了ResponseStatus注解,主要是為了替換狀態(tài)碼
if (responseStatus != null) {
HttpStatus statusCode = responseStatus.code();
String reason = responseStatus.reason();
// 該執(zhí)行方法存在該ResponseStatus的注解氧秘,獲取狀態(tài)碼和原因
// 不過該具體使用的時(shí)候年鸳,只能是HttpStatus枚舉類中的選項(xiàng),默認(rèn)是500錯(cuò)誤
if (!StringUtils.hasText(reason)) {
// 沒有具體原因丸相,就只設(shè)置狀態(tài)碼搔确,否則就返回錯(cuò)誤碼
webRequest.getResponse().setStatus(statusCode.value());
}
else {
webRequest.getResponse().sendError(statusCode.value(), reason);
}
// 設(shè)置當(dāng)前的請(qǐng)求屬性,名字為模板和狀態(tài),值就是狀態(tài)碼
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, statusCode);
this.responseArgumentUsed = true;
}
// 如果存在自定義的ModelAndView解析
if (customModelAndViewResolvers != null) {
for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
ModelAndView mav = mavResolver.resolveModelAndView(
handlerMethod, handlerType, returnValue, implicitModel, webRequest);
// 遍歷自定義ModelAndView解析器膳算,選擇合適的mv座硕,并且不是初始化的視圖類
// 這個(gè)是由用戶自定義的實(shí)現(xiàn)類,并且切記記得適配器的設(shè)置畦幢,別沒弄好導(dǎo)致無合適的適配器的錯(cuò)誤
// ModelAndView UNRESOLVED = new ModelAndView();
if (mav != ModelAndViewResolver.UNRESOLVED) {
return mav;
}
}
}
// 返回的類型是HttpEntity坎吻、string、ModelAndView宇葱、Model瘦真、View、Map
if (returnValue instanceof HttpEntity) {
// 返回值是HttpEntity類型的數(shù)據(jù)黍瞧,不需要合適的視圖
// 更多的是返回json數(shù)據(jù)
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
// 方法包含了ResponseBody注解诸尽,直接把returnValue 塞入response
handleResponseBody(returnValue, webRequest);
return null;
}
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
}
else if (returnValue instanceof String) {
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
else if (returnValue == null) {
// Either returned null or was 'void' return.
if (this.responseArgumentUsed || webRequest.isNotModified()) {
return null;
}
else {
// Assuming view name translation...
return new ModelAndView().addAllObjects(implicitModel);
}
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
// Assume a single model attribute...
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else {
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
在這其中的代碼快customModelAndViewResolvers,更多的細(xì)節(jié)可以看看Spring Controller層記錄日志配置的實(shí)踐demo
視圖渲染
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
// 有異常印颤,不能正常顯示請(qǐng)求內(nèi)容了
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);
// 異常處理返回的mv您机,異常mv配置存放在handlerExceptionResolvers
errorView = (mv != null);
}
}
// 有可用的mv
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
// render就是渲染,回填數(shù)據(jù)到response中去
if (errorView) {
// 如果有非ModelAndViewDefiningException年局,則清除請(qǐng)求的屬性信息
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
// 沒有可用的視圖信息
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
渲染render
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()) {
// mv的視圖名字是string類型际看,則需要找到真正的視圖
// 需要使用到local字段信息
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
// 沒有找到有效的視圖,報(bào)錯(cuò)提示
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// 否則就被認(rèn)為是View對(duì)象矢否,直接獲取就可以了
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) {
// 從mv中獲取http狀態(tài)碼
response.setStatus(mv.getStatus().value());
}
// 來到了真正的渲染仲闽,所有的數(shù)據(jù)已經(jīng)準(zhǔn)備就緒
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;
}
}
其中的resolve
View 渲染
View本身是一個(gè)接口類,spring根據(jù)不同的業(yè)務(wù)實(shí)現(xiàn)了多個(gè)不同的View實(shí)體類僵朗,包含了html赖欣、pdf、Excel等验庙。
AbstractView 抽象視圖類
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
由各自的子類去實(shí)現(xiàn)renderMergedOutputModel方法