1.1 gradle搭建源码调试环境

1)搭建gradle环境

4个步骤

1、File-New-Module

抉择java和web

2、填写包信息


3、存储门路

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 tomcatRunbuildscript {    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-8tasks.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创立结束

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;@Controllerpublic 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 NameDepends OnTypeDescription
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

控制台失常输入

形式二:集成到idea中启动

设置

即可点击运行

运行胜利

形式三:

idea左边找到gradle的task,间接双击,这个爽~

拜访MVC我的项目

留神:spring-test-mvc是我的项目的名称
http://localhost:8080/spring-test-mvc/index

成果如下

5)源码调试配置

idea里的调试

简略,debug模式启动tomcat即可

近程调试模式

重要

想要近程debug,须要应用下面的办法二,因为debug启动须要设置gradle的环境变量,

即运行gradle命令的时候插入一些参数命令。

减少debug参数,对外裸露5005端口,即监听5005端口。

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

在配置Remote;监听5005端口

点击+号,创立Remote;默认配置即可


最初一步

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-...

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

不要求记住


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看起

总结:办法调用关系(伪代码)

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 mvcFrameworkServlet 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!}

代码查找的门路:

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.getModeAndViewmv = 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);        }    }

如果本文对您有帮忙,欢送关注点赞`,您的反对是我保持创作的能源。

转载请注明出处!