Spring MVC URL映射 學(xué)習(xí)(下)

接收到一個(gè)新的請(qǐng)求之后贬媒,spring就會(huì)去根據(jù)請(qǐng)求的URL信息選擇具體的代碼塊去執(zhí)行操作不铆,如圖就是接收到一個(gè)新的請(qǐng)求調(diào)用圖四敞,從Tomcat開始直到把請(qǐng)求分發(fā)到spring中桐玻,最后到了doDispatch方法


image

本篇學(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;
}

如圖所示就是


image

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


image

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


image

獲取適配器

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;
}
image
    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方法

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顶吮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子粪薛,更是在濱河造成了極大的恐慌悴了,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件违寿,死亡現(xiàn)場(chǎng)離奇詭異湃交,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)陨界,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門巡揍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人菌瘪,你說我怎么就攤上這事腮敌≮宓保” “怎么了?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵糜工,是天一觀的道長(zhǎng)弊添。 經(jīng)常有香客問我,道長(zhǎng)捌木,這世上最難降的妖魔是什么油坝? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮刨裆,結(jié)果婚禮上澈圈,老公的妹妹穿的比我還像新娘。我一直安慰自己帆啃,他們只是感情好瞬女,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著努潘,像睡著了一般诽偷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疯坤,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天报慕,我揣著相機(jī)與錄音,去河邊找鬼压怠。 笑死眠冈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的刑峡。 我是一名探鬼主播洋闽,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼玄柠,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼突梦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起羽利,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤宫患,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后这弧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娃闲,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年匾浪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了皇帮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛋辈,死狀恐怖属拾,靈堂內(nèi)的尸體忽然破棺而出将谊,到底是詐尸還是另有隱情,我是刑警寧澤渐白,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布尊浓,位于F島的核電站,受9級(jí)特大地震影響纯衍,放射性物質(zhì)發(fā)生泄漏栋齿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一襟诸、第九天 我趴在偏房一處隱蔽的房頂上張望瓦堵。 院中可真熱鬧,春花似錦歌亲、人聲如沸谷丸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)刨疼。三九已至,卻和暖如春鹅龄,著一層夾襖步出監(jiān)牢的瞬間揩慕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工扮休, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迎卤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓玷坠,卻偏偏與公主長(zhǎng)得像蜗搔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子八堡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容