Stories

Detail Return Return

愛上源碼,重學Spring MVC深入 - Stories Detail

1.1 gradle搭建源碼調試環境

1)搭建gradle環境

4個步驟

1、File-New-Module

選擇java和web

file

2、填寫包信息

file
3、存儲路徑

file

2)增加起步依賴

依賴的項目,直接複製粘貼上去

1、對spring的依賴

2、對MVC的依賴

3、對Tomcat插件的依賴

build.gradle

group 'com.spring.test'
version '5.0.2.RELEASE'

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'com.bmuschko.tomcat' //tomcat: 插件
// tomcat: 以下配置會在第一次啓動時下載插件二進制文件
//在項目根目錄中執行gradle tomcatRun
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.bmuschko:gradle-tomcat-plugin:2.5'
    }
}
// 配置阿里源
allprojects {
    repositories {
        maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
    }
}


dependencies {
    testCompile group: 'org.testng', name: 'testng', version: '6.14.3'
    runtime 'javax.servlet:jstl:1.1.2' // Servlet容器必需
    compile(project(':spring-context'))
    compile(project(':spring-web'))
    compile(project(':spring-webmvc'))

    // tomcat: 將Tomcat運行時庫添加到配置tomcat中: (此處為Tomcat9)
    def tomcatVersion = '9.0.1'
    tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
            "org.apache.tomcat.embed:tomcat-embed-logging-juli:9.0.0.M6",
            "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
}

// tomcat: 一些協議設置(注意,這裏必須加上,不然會拋tomcat的異常,僅限tomcat9)
tomcat {
    httpProtocol = 'org.apache.coyote.http11.Http11Nio2Protocol'
    ajpProtocol  = 'org.apache.coyote.ajp.AjpNio2Protocol'
}



// UTF-8
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

3)MVC代碼編寫

前提:

增加WEB-INF目錄和Web.xml

1、打開File - Proect Structrue

2、選中剛才的mvc項目,展開,選中web gradle , 到右邊 點擊加號

3、確認路徑

spring-mvc-test\src\main\webapp\WEB-INF\web.xml

WEB-INF和xml創建完畢
file

webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Spring MVC配置 -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc-servlet.xml</param-value>
            <!--<param-value>/WEB-INF/mvc-servlet.xml</param-value>-->
        </init-param>
        <!-- load-on-startup元素標記容器是否在啓動的時候就加載這個servlet(實例化並調用其init()方法) -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

resources/mvc-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓註解掃描 -->
    <context:component-scan base-package="com.spring.mvc.test"/>
    <!-- 視圖解析器對象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <!--<property name = "prefix" value="/WEB-INF/"></property>-->
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 開啓SpringMVC框架註解的支持 -->
    <mvc:annotation-driven/>
    <!--靜態資源(js、image等)的訪問-->
    <mvc:default-servlet-handler/>

</beans>

webapp/index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>SpringMvc源碼深入剖析</title>
  </head>
  <body>
  Gradle構建Spring MVC例子....
  </body>
</html>

MvcController.java

package com.spring.mvc.test;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MvcController {

    @RequestMapping("/index")
    public ModelAndView getModeAndView() {
        //創建一個模型視圖對象
        ModelAndView mav = new ModelAndView("index");
        return mav;
    }
    @RequestMapping("/text")
    @ResponseBody
    public String text() {
        return "Text...";
    }

}

4)啓動MVC項目

兩種啓動方式

方式一:外掛啓動

idea環境外面啓動(項目根目錄下運行 gradle + task name)

Task Name Depends On Type Description
tomcatRun - TomcatRun 啓動Tomcat實例並將Web應用程序部署到該實例。
tomcatRunWar - TomcatRunWar 啓動Tomcat實例並將WAR部署
tomcatStop - TomcatStop 停止Tomcat實例
tomcatJasper - TomcatJasper 運行JSP編譯器並使用Jasper將JSP頁面轉換為Java源代碼。

在項目根目錄中執行gradle tomcatRun

#動Tomcat實例並將Web應用程序部署到該實例
gradle  tomcatRun
#停止Tomcat實例
gradle tomcatStop

控制枱正常輸出

file

方式二:集成到idea中啓動

file

設置

file

即可點擊運行

file

運行成功

file

方式三:

idea右邊找到gradle的task,直接雙擊,這個爽~

file

訪問MVC項目

注意:spring-test-mvc是項目的名稱
http://localhost:8080/spring-test-mvc/index

效果如下

file

5)源碼調試配置

idea裏的調試

簡單,debug模式啓動tomcat即可

file

遠程調試模式

重要

想要遠程debug,需要使用上面的方法二,因為debug啓動需要設置gradle的環境變量,

即運行gradle命令的時候插入一些參數命令。

增加debug參數,對外暴露5005端口,即監聽5005端口。

-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005

file

在配置Remote;監聽5005端口

點擊+號,創建Remote;默認配置即可

file
最後一步

1、先運行tomcat

2、再運行remote

http://localhost:8080/spring-test-mvc/index

打上斷點試試!

包括我們之前ioc裏的bean創建等地方,隨便打。

1.2 MVC工作原理和繼承關係

1)MVC底層工作原理

目標:認識SpringMVC的工作原理(對照源碼),如何找到對應的Controller,進行頁面渲染的

步驟:11步

源頭:http://localhost:8080/spring-...

file

SpringMVC工作原理

1、DispatcherServlet(前端控制器) 是個servlet,負責接收Request 並將Request 轉發給對應的處理組件。

2、 HanlerMapping (處理器映射器)是SpringMVC 中完成url 到Controller 映射的組件。DispatcherServlet 從HandlerMapping 查找處理Request 的Controller,

3、HanlerMapping 返回一個執行器鏈(url 到Controller 映射的組件)給DispatcherServlet

4、DispatcherServlet請求處理器適配器HandlerAdapter

5、處理器適配器HandlerAdapter去訪問我們的handler(controller)

6、handler(controller)返回ModelAndView給處理器適配器HandlerAdapter

7、處理器適配器HandlerAdapter返回ModelAndView給DispatcherServlet

8、DispatcherServlet請求ViewResolver視圖解析器

9、ViewResolver視圖解析器返回view給DispatcherServlet

10、DispatcherServlet請求view做頁面解析和渲染

11、view將渲染好的數據返回給DS,DS將渲染好的字符流給client,看到了頁面!

2)MVC核心類繼承關係

目標:簡單認識MVC的繼承關係

tips

不要求記住

file
DispatcherServlet 前端總控制器(webmvc源碼)

FrameworkServlet (webmvc源碼)

HttpServletBean 是的一個簡單擴展類((webmvc源碼)

HttpServlet(servlet API , 已經離開了spring mvc的控制範圍)

1.3 Spring MVC源碼深入剖析

引言:
當前源碼講解思路
1、斷點調試
2、流程圖對照
3、繼承關係對照

1.3.1 MVC啓動階段

注意,這個階段沒法debug,我們從servlet規範去直接看源碼

下面的請求階段,再詳細debug請求鏈路的完整過程

web.xml回顧

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Spring MVC配置 -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc-servlet.xml</param-value>
            <!--<param-value>/WEB-INF/mvc-servlet.xml</param-value>-->
        </init-param>
        <!-- load-on-startup元素標記容器是否在啓動的時候就加載這個servlet(實例化並調用其init()方法) -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

     <!--初始化Spring icC容器-->
    <!--<context-param>-->
        <!--<param-name>contextConfigLocation</param-name>-->
    <!--默認的路徑是/WEB-INF/applicationontext.xml,下面多個xml使用,分割-->
        <!--<param-value>classpath: applicationContext-ZH.xml</param-value>-->
    <!--</context-param>-->
    <!--要使用Spring的IoC容器-->
    <!--<listener>-->
        <!--<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
    <!--</listener>-->

</web-app>

從上面的配置,我們可以看出,web.xml中的DS是一個servlet,那就從java web的servlet規範説起

上面類關係,我們説過,springmvc的範疇裏,最頂層的是 HttpServletBean 繼承的 標準 HttpServlet

1、ioC Bean初始化

org.springframework.web.servlet.HttpServletBean#init

2、9大組件初始化(ioC)

org.springframework.web.servlet.HttpServletBean#init

啓動:servlet規範,init方法被容器調用

在servlet實例化後,被容器調用一次init方法,所以啓動我們找到mvc裏的父類從init看起

file

總結:方法調用關係(偽代碼)

HttpServletBean{
  init(){
    protected initServletBean();
  }
}

FrameworkServlet extends HttpServletBean{
  @Override
  initServletBean(){
    initWebApplicationContext(){
      WebApplicationContext wac = createWebApplicationContext(rootContext);
      protected onRefresh(wac); 
    }
  }
}

DispatcherServlet extends FrameworkServlet{
  onRefresh(wac){
    initStrategies(wac){
          //多文件上傳的組件
        initMultipartResolver(context);
        //初始化本地語言環境
        initLocaleResolver(context);
        //初始化模板處理器
        initThemeResolver(context);
        //初始化處理器映射器
        initHandlerMappings(context);
        //初始化處理器適配器
        initHandlerAdapters(context);
        //初始化異常攔截器
        initHandlerExceptionResolvers(context);
        //初始化視圖預處理器
        initRequestToViewNameTranslator(context);
        //初始化視圖轉換器
        initViewResolvers(context);
        //FlashMap 管理器
        initFlashMapManager(context);
    }
  }
}

1.3.2 MVC請求階段

需求:我們在瀏覽器輸入http://localhost:8080/spring-...,背後到底做了哪些事情

目標:MVC如何通過一個url就能找到我們的controller,並返回數據

1、斷點調試
2、流程圖對照
3、繼承關係對照

流程圖解:

標準Servlet(回顧tomcat源碼裏,容器最後調的是wrapper的 service 方法)

偽代碼

interface Servlet{
    service()  // 1  , 標準servlet規範的入口
}

HttpServlet implements Servlet{
    public service(ServletRequest req, ServletResponse res){
        //轉成 HttpServletRequest
        protected service(req,res);  // 2
    }
    protected service(HttpServletRequest req, HttpServletResponse resp){
        if(isGet){
            protected doGet()  // 4
        }        
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse resp);  // 5
}

//spring mvc

FrameworkServlet extends HttpServlet{
    @Override
    service(){
        super.service();  // 3
    }
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp){
        processRequest(request, response){
            protected doService(request, response); // 6
        }    
    }
}

DispatcherServlet extends FrameWorkServlet{
    protected doService(request, response);  //  7  , here!
}

代碼查找的路徑:

file

tips:

spring mvc的 FrameworkServlet ,這是我們源碼跟蹤的入口

項目啓動

訪問

http://localhost:8080/spring-test-mvc/index
上圖的初始化流程在源碼中是怎麼流轉的呢?

入口:開啓請求的大門

org.springframework.web.servlet.FrameworkServlet:

java web標準告訴我們,request的get會交給標準 HttpServlet的doGet方法

而這個類FrameworkServlet,是HttpServlet的子類,覆蓋了上述的doGet,

所以,請求進入spring的第一入口,就在這裏!!!

1)org.springframework.web.servlet.FrameworkServlet#doGet

調用到了org.springframework.web.servlet.FrameworkServlet#doGet

    //get請求調用
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

2)org.springframework.web.servlet.FrameworkServlet#processRequest

//    重點關注:doService
    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 {
            //重點查看,跳到DispatcherServlet 類中(子類重寫)
            doService(request, response);
        } catch (ServletException | 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);
        }
    }

3)org.springframework.web.servlet.DispatcherServlet#doService

    //重寫父類
    //重點關注  doDispatch(request, response);
    @Override
    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<>();
            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());

        if (this.flashMapManager != null) {
            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);
                }
            }
        }
    }

進入核心

4)org.springframework.web.servlet.DispatcherServlet#doDispatch

//    Spring MVC的最核心代碼
    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);

//                根據當前的請求去拿一個Handler.這個Handler其實就是我們的控制器,進入!!!!!
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 處理器適配器,9大組件初始化
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                //get方法為true
                boolean isGet = "GET".equals(method);
                //method為get
                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;
                }

                // 執行我們的業務控制器方法,com.spring.mvc.test.MvcController.getModeAndView
                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);
                }
            }
        }
    }

5)org.springframework.web.servlet.DispatcherServlet#getHandler

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //不止一個,比如BeanNameHandlerMapping、SimpleUrlHandlerMapping,還有我們需要的RequestHandlerMapping
        //在9個組件初始化的時候賦值
        if (this.handlerMappings != null) {
            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;
    }

org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            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");
    }

6) 調用業務Controller

// 執行我們的業務控制器方法,com.spring.mvc.test.MvcController.getModeAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInterna

    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

    @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) {
            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;
    }

7)org.springframework.web.servlet.DispatcherServlet#processDispatchResult

//1、請求視圖解析器,解析成view
    //2、執行頁面渲染
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
            @Nullable 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);
            }
        }

        //視圖渲染,響應視圖
        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);
        }
    }

如果本文對您有幫助,歡迎關注點贊`,您的支持是我堅持創作的動力。

轉載請註明出處!

user avatar king_wenzhinan Avatar u_16297326 Avatar xiaoniuhululu Avatar journey_64224c9377fd5 Avatar sofastack Avatar u_16502039 Avatar u_13529088 Avatar seazhan Avatar u_16769727 Avatar lenglingx Avatar u_15702012 Avatar jiangyi Avatar
Favorites 69 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.