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 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 创立结束
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
控制台失常输入
形式二:集成到 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 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!
}
代码查找的门路:
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);
}
}
如果本文对您有帮忙,欢送
关注
和点赞
`,您的反对是我保持创作的能源。转载请注明出处!