SpringMVC源码-接口请求执行流程,包含九大内置组件的实例化初始化,拦截器调用,页面渲染等源码讲解

ops/2024/10/20 7:45:30/

一、上传文件功能的实现:

前端JSP代码:
form 表单提交,enctype为multipart/form-data,请求方式POST

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%pageContext.setAttribute("ctx",request.getContextPath());
%>
<html>
<head><title>Title</title>
</head>
<body>
<form action="${ctx}/fileupload" enctype="multipart/form-data" method="post">描述:<input type="text" name="desc"><br><br>文件:<input type="file" name="file"><br><br><input type="submit" value="上传">
</form>
</body>
</html>

FileUploadController 的 fileupload方法:

java"> import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;@Controller
public class FileUploadController {@RequestMapping("fileupload")public String upload(MultipartFile file, HttpServletRequest request,String desc) throws IOException {System.out.println(desc);if (file.isEmpty()){return "false";}String path = request.getServletContext().getRealPath("/WEB-INF/file");String fileName = file.getOriginalFilename();File filePath = new File(path,fileName);if (!filePath.getParentFile().exists()){filePath.getParentFile().mkdir();}file.transferTo(filePath);return "success";}
}

二、上传文件使用的组件

MultipartResolver

MultipartResolver用于处理上传请求,处理方式是将普通的request包装成MultipartHttpServletRequest,可以直接调用getFile方法来获取File,如果上传多个文件,可以调用getFileMap来处理。

java">public interface MultipartResolver {/*** 判断是否是上传请求*/boolean isMultipart(HttpServletRequest request);/*** 将request请求包装成MultipartHttpServletRequest*/MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;/*** 处理完成之后清理上传过程中产生的临时资源*/void cleanupMultipart(MultipartHttpServletRequest request);}

类继承关系,需要实现上面三个方法:
在这里插入图片描述

CommonsMultipartResolver

<a href="https://commons.apache.org/proper/commons-fileupload">Apache Commons FileUpload</a>

依赖的一个apache的组件jar包

初始化九大内置组件的时候,没有对上传组件设置默认值,所以要提前交给spring生成。
放在SpringMVC的配置文件里,等待加载完成。

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="maxUploadSize" value="17367648787"></property><property name="defaultEncoding" value="UTF-8"></property></bean>

三、上传文件执行流程

1.检测请求是否为上传请求,如果是则通过multipartResolver将其封装成MultipartHttpServletRequest对象
在这里插入图片描述

确定请求是否包含多部分内容的实用程序方法。注意:在FileUpload 1.1发布之后,这个方法将被移动到ServletFileUpload类中。不幸的是,由于此方法是静态的,因此在删除此方法之前不可能提供其替代品。

在这里插入图片描述
MULTIPART和enctype=“multipart/form-data” 对应,证明是上传文件的请求。
在这里插入图片描述

this.multipartResolver.isMultipart(request)返回true,

在这里插入图片描述
进入

// 将 HttpServletRequest 请求封装成 MultipartHttpServletRequest 对象,解析请求里面的参数以及文件
return this.multipartResolver.resolveMultipart(request);

CommonsMultipartResolver#resolveMultipart

java">	// 处理懒加载,如果为true的话,会将解析请求的操作放到DefaultMultipartHttpServletRequest的initializeMultipart方法中,只有在实际调用的时候才会被调用// 如果值为false的话,那么会先调用parseRequest方法来处理request请求,然后将处理的结果放到DefaultMultipartHttpServletRequestprivate boolean resolveLazily = false;public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {Assert.notNull(request, "Request must not be null");if (this.resolveLazily) {return new DefaultMultipartHttpServletRequest(request) {@Overrideprotected void initializeMultipart() {//MultipartParsingResult parsingResult = parseRequest(request);setMultipartFiles(parsingResult.getMultipartFiles());setMultipartParameters(parsingResult.getMultipartParameters());setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());}};}else {MultipartParsingResult parsingResult = parseRequest(request);return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());}}

MultipartParsingResult parsingResult = parseRequest(request);

java">	/*** 对请求进行处理,转成MultipartParsingResult对象** Parse the given servlet request, resolving its multipart elements.* @param request the request to parse* @return the parsing result* @throws MultipartException if multipart resolution failed.*/protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {// 从请求中读出当前请求的编码String encoding = determineEncoding(request);//按照请求的编码,获取一个FileUpload对象,装载到CommonsFileUploadSupport的property属性都会被装入这个对象中//prepareFileUpload是继承自CommonsFileUploadSupport的函数,会比较请求的编码和XML中配置的编码,如果不一样,会拒绝处理FileUpload fileUpload = prepareFileUpload(encoding);try {// 对请求中的multipart文件进行具体的处理List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);return parseFileItems(fileItems, encoding);}catch (FileUploadBase.SizeLimitExceededException ex) {throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);}catch (FileUploadBase.FileSizeLimitExceededException ex) {throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);}catch (FileUploadException ex) {throw new MultipartException("Failed to parse multipart servlet request", ex);}}

// 对请求中的multipart文件进行具体的处理
List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);

java">public List<FileItem> parseRequest(RequestContext ctx)throws FileUploadException {List<FileItem> items = new ArrayList<FileItem>();boolean successful = false;try {FileItemIterator iter = getItemIterator(ctx);FileItemFactory fac = getFileItemFactory();if (fac == null) {throw new NullPointerException("No FileItemFactory has been set.");}while (iter.hasNext()) {final FileItemStream item = iter.next();// Don't use getName() here to prevent an InvalidFileNameException.final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),item.isFormField(), fileName);items.add(fileItem);try {Streams.copy(item.openStream(), fileItem.getOutputStream(), true);} catch (FileUploadIOException e) {throw (FileUploadException) e.getCause();} catch (IOException e) {throw new IOFileUploadException(format("Processing of %s request failed. %s",MULTIPART_FORM_DATA, e.getMessage()), e);}final FileItemHeaders fih = item.getHeaders();fileItem.setHeaders(fih);}successful = true;return items;} catch (FileUploadIOException e) {throw (FileUploadException) e.getCause();} catch (IOException e) {throw new FileUploadException(e.getMessage(), e);} finally {if (!successful) {for (FileItem fileItem : items) {try {fileItem.delete();} catch (Exception ignored) {// ignored TODO perhaps add to tracker delete failure list somehow?}}}}}

获取到传递得文件
在这里插入图片描述

java">	/*** 对请求进行处理,转成MultipartParsingResult对象** Parse the given servlet request, resolving its multipart elements.* @param request the request to parse* @return the parsing result* @throws MultipartException if multipart resolution failed.*/protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {// 从请求中读出当前请求的编码String encoding = determineEncoding(request);//按照请求的编码,获取一个FileUpload对象,装载到CommonsFileUploadSupport的property属性都会被装入这个对象中//prepareFileUpload是继承自CommonsFileUploadSupport的函数,会比较请求的编码和XML中配置的编码,如果不一样,会拒绝处理FileUpload fileUpload = prepareFileUpload(encoding);try {// 对请求中的multipart文件进行具体的处理List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);return parseFileItems(fileItems, encoding);}catch (FileUploadBase.SizeLimitExceededException ex) {throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);}catch (FileUploadBase.FileSizeLimitExceededException ex) {throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);}catch (FileUploadException ex) {throw new MultipartException("Failed to parse multipart servlet request", ex);}}

parseFileItems

java">	/*** Parse the given List of Commons FileItems into a Spring MultipartParsingResult,* containing Spring MultipartFile instances and a Map of multipart parameter.* @param fileItems the Commons FileItems to parse* @param encoding the encoding to use for form fields* @return the Spring MultipartParsingResult* @see CommonsMultipartFile#CommonsMultipartFile(org.apache.commons.fileupload.FileItem)*/protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {// 保存上传的文件MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<>();// 保存参数Map<String, String[]> multipartParameters = new HashMap<>();// 保存参数的contentTypeMap<String, String> multipartParameterContentTypes = new HashMap<>();// Extract multipart files and multipart parameters.// 将fileItems分为文件和参数两类,并设置到对应的mapfor (FileItem fileItem : fileItems) {// 如果是参数类型if (fileItem.isFormField()) {String value;String partEncoding = determineEncoding(fileItem.getContentType(), encoding);try {value = fileItem.getString(partEncoding);}catch (UnsupportedEncodingException ex) {if (logger.isWarnEnabled()) {logger.warn("Could not decode multipart item '" + fileItem.getFieldName() +"' with encoding '" + partEncoding + "': using platform default");}value = fileItem.getString();}String[] curParam = multipartParameters.get(fileItem.getFieldName());if (curParam == null) {// simple form field// 单个参数multipartParameters.put(fileItem.getFieldName(), new String[] {value});}else {// array of simple form fields// 数组参数String[] newParam = StringUtils.addStringToArray(curParam, value);multipartParameters.put(fileItem.getFieldName(), newParam);}// 保存参数的contentTypemultipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());}else {// multipart file field// 如果是文件类型CommonsMultipartFile file = createMultipartFile(fileItem);multipartFiles.add(file.getName(), file);LogFormatUtils.traceDebug(logger, traceOn ->"Part '" + file.getName() + "', size " + file.getSize() +" bytes, filename='" + file.getOriginalFilename() + "'" +(traceOn ? ", storage=" + file.getStorageDescription() : ""));}}return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);}

parseFileItems得调用堆栈:

java">parseFileItems:301, CommonsFileUploadSupport (org.springframework.web.multipart.commons)
parseRequest:168, CommonsMultipartResolver (org.springframework.web.multipart.commons)
resolveMultipart:145, CommonsMultipartResolver (org.springframework.web.multipart.commons)
checkMultipart:1286, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1088, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doPost:971, FrameworkServlet (org.springframework.web.servlet)
service:681, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

回到CommonsMultipartResolver#resolveMultipart

在这里插入图片描述

回到DispatcherServlet得doDispatch方法,此时processedRequest = checkMultipart(request);执行结束,processedRequest变为:
在这里插入图片描述
设置上传请求的标志,此时只是请求处理,还没进行正式处理。

四、SpringMVC定义Controller得几种方式:

1、使用@Controller注解

java">import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;@Controller
public class FileUploadController {@RequestMapping("fileupload")public String upload(MultipartFile file, HttpServletRequest request,String desc) throws IOException {System.out.println(desc);if (file.isEmpty()){return "false";}String path = request.getServletContext().getRealPath("/WEB-INF/file");String fileName = file.getOriginalFilename();File filePath = new File(path,fileName);if (!filePath.getParentFile().exists()){filePath.getParentFile().mkdir();}file.transferTo(filePath);return "success";}
}

2、实现org.springframework.web.servlet.mvc.Controller接口

java">import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class Test01 implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {System.out.println("Controller接口 ..............");return null;}
}

3、实现org.springframework.web.HttpRequestHandler接口

java">import org.springframework.web.HttpRequestHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class Test02 implements HttpRequestHandler {@Overridepublic void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {System.out.println("HttpRequestHandler.........");}
}

总结: 使用2、3两种,要在配置文件写bean标签。/开头,表明这是个请求映射路径。1对应得是AbstractHandlerMethodMapping,2、3对应得是AbstractUrlHandlerMapping

java">    <bean name="/test01" class="com.mashibing.controller.testController.Test01"></bean><bean id="/test02" class="com.mashibing.controller.testController.Test02"></bean>

五、HandlerMapping 处理器映射器

    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\org.springframework.web.servlet.function.support.RouterFunctionMapping

在这里插入图片描述

获得请求对应的HandlerExecutionChain对象(HandlerMethod和HandlerInterceptor拦截器们)
mappedHandler = getHandler(processedRequest);//请求对应的是哪个controller
在这里插入图片描述
处理器映射器默认有三个先找AbstractUrlHandlerMapping的子类beanNameUrlHandlerMapping:
AbstractUrlHandlerMapping
在这里插入图片描述
这里的mapping映射器是beanNameUrlHandlerMapping,这个映射器是AbstractUrlHandlerMapping的子类,这里的handlerMap如下:
在这里插入图片描述
handlerMap是什么时候赋值的呢?
SpringMVC源码-AbstractUrlHandlerMapping处理器映射器的handlerMap如何实现Controller接口的方式定义的路径存储进去
需要的是FileUploadController在AbstractUrlHandlerMapping这里匹配不到合适的。
同时这步操作也完成了拦截器定义加载

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
AbstractHandlerMethodMapping类的mappingRegistry什么时候赋值的呢?

SpringMVC源码-AbstractHandlerMethodMapping处理器映射器将@Controller修饰类方法存储到处理器映射器

在这里插入图片描述
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);找到对应的controller
在这里插入图片描述

六、拦截器和HandlerAdapter处理器适配器

getHandlerInternal执行结束,开始执行HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
在这里插入图片描述
getHandlerExecutionChain 匹配合适的拦截器生成拦截器链

java">	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {// 创建 HandlerExecutionChain 对象HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));// 获得请求路径String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);// 遍历 adaptedInterceptors 数组,获得请求匹配的拦截器for (HandlerInterceptor interceptor : this.adaptedInterceptors) {// 需要匹配,若路径匹配,则添加到 chain 中if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}// 无需匹配,直接添加到 chain 中else {chain.addInterceptor(interceptor);}}return chain;}

在这里插入图片描述
获取HandlerAdapter

// 获得当前handler对应的HandlerAdapter对象 controller或者控制器有多种不同的实现方式
为了方便后续过程中调用 使用适配器模式来 解决 HandlerAdapter ha =
getHandlerAdapter(mappedHandler.getHandler());

SpringMVC源码-处理器适配器HandlerAdapter讲解

mappedHandler = getHandler(processedRequest);//请求对应的是哪个controller,以及需要被执行的拦截器。
在这里插入图片描述
这里配置的拦截器是对所有方法生效,执行拦截器是在下面:
在这里插入图片描述
进入方法:
在这里插入图片描述

进入HandlerMappingInterceptor拦截器执行preHandle方法
在这里插入图片描述
如果这里返回false:
进入triggerAfterCompletion
在这里插入图片描述
afterCompletion在整个请求执行完之后执行,也就是DispatcherServlet视图渲染之后执行,这个方法主要作用是用于清理资源,至此,拦截器的前置执行方法也被讲解完毕。

七、执行处理器适配器HandlerAdapter的handle方法

执行controller里的方法
在这里插入图片描述

java">handleInternal:825, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:90, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1127, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

在这里插入图片描述
RequestMappingHandlerAdapter的invokeHandlerMethod方法:

java">protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {// 使用request和response创建ServletWebRequest对象ServletWebRequest webRequest = new ServletWebRequest(request, response);try {// 创建WebDataBinderFactory对象,此对象用来创建WebDataBinder对象,进行参数绑定,// 实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinderWebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);//返回的是@InitBinder注解修饰的方法的集合// 创建ModelFactory对象,此对象主要用来处理model,主要是两个功能,1是在处理器具体处理之前对model进行初始化,2是在处理完请求后对model参数进行更新ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// 创建ServletInvocableHandlerMethod对象,并设置其相关属性,实际的请求处理就是通过此对象来完成的,参数绑定、处理请求以及返回值处理都在里边完成ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 设置参数处理器argumentResolvers 在该适配器初始化的时候调用afterPropertiesSet方法完成赋值if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 设置返回值处理器if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}// 设置参数绑定工厂对象invocableMethod.setDataBinderFactory(binderFactory);// 设置参数名称发现器invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 创建ModelAndViewContainer对象,用于保存model和View对象ModelAndViewContainer mavContainer = new ModelAndViewContainer();// 将flashmap中的数据设置到model中 FlashMap只适用于重定向跳转传参mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));// 使用modelFactory将sessionAttributes和注释了@ModelAttribute的方法的参数设置到model中modelFactory.initModel(webRequest, mavContainer, invocableMethod);// 根据配置对ignoreDefaultModelOnRedirect进行设置mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);// 创建AsyncWebRequest异步请求对象AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);// 创建WebAsyncManager异步请求管理器对象WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);// 如果当前异步请求已经处理并得到结果,则将返回的结果放到mavContainer对象中,然后将invocable对象进行包装转换,转成需要的执行对象然后开始执行if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});// 转换具体的invocable执行对象invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 执行调用invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}// 处理完请求后的后置处理,此处一共做了三件事,// 1、调用ModelFactory的updateModel方法更新model,包括设置SessionAttribute和给Model设置BinderResult// 2、根据mavContainer创建了ModelAndView// 3、如果mavContainer里的model是RedirectAttributes类型,则将其设置到FlashMapreturn getModelAndView(mavContainer, modelFactory, webRequest);}finally {// 标记请求完成webRequest.requestCompleted();}}

八、@ControllerAdvice和 @InitBinder注解源码层面讲解

getDataBinderFactory
创建WebDataBinderFactory对象,此对象用来创建WebDataBinder对象,进行参数绑定,实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinder
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);//返回的是@InitBinder注解修饰的方法的集合

java">	private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {Class<?> handlerType = handlerMethod.getBeanType();// 检查当前Handler中的initBinder方法是否已经存在于缓存中Set<Method> methods = this.initBinderCache.get(handlerType);// 如果没有则查找并设置到缓冲中if (methods == null) {methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);// 将当前Controller中所有被@InitBinder注解修饰的方法都获取到this.initBinderCache.put(handlerType, methods);}// 定义保存InitBinder方法的临时变量 对当前controller有效List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();// Global methods first  @ControllerAdvice修饰的类里的@InitBinder注解修饰的方法// 将所有符合条件的全局InitBinder方法添加到initBinderMethodsthis.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {Object bean = controllerAdviceBean.resolveBean();for (Method method : methodSet) {initBinderMethods.add(createInitBinderMethod(bean, method));}}});// 将当前handler中的initBinder方法添加到initBinderMethodsfor (Method method : methods) {//当前controller里@InitBinder注解修饰的方法和@ControllerAdvice修饰的类里的@InitBinder注解修饰的方法合并到一起 但是不会去重 因为这里的容器是list// 创建当前方法对应的bean对象Object bean = handlerMethod.getBean();// 将method适配为可执行的invocableHandlerMethodinitBinderMethods.add(createInitBinderMethod(bean, method));}// 创建DataBinderFactory并返回return createDataBinderFactory(initBinderMethods);}

InitBinderController

java">/*** InitBinder注解作用于Controller中的方法,表示为当前控制器注册一个属性编辑器* 对webDataBinder进行初始化且只对当前的Controller有效***/@Controller
public class InitBinderController {// 类型转换@InitBinderpublic void initBinder(WebDataBinder binder){DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");CustomDateEditor dateEditor = new CustomDateEditor(df,true);binder.registerCustomEditor(Date.class,dateEditor);}// 属性编辑器@RequestMapping("/param")public String getFormatDate( Date data,  Map<String,Object> map){System.out.println(data);map.put("name","zhangsan");map.put("age",12);map.put("date",data);return "map";}// 参数绑定@InitBinder("user")public void init1(WebDataBinder binder){binder.setFieldDefaultPrefix("u.");}@InitBinder("stu")public void init2(WebDataBinder binder){binder.setFieldDefaultPrefix("s.");}@RequestMapping("/getBean")public ModelAndView getBean(User user, @ModelAttribute("stu") Student stu){System.out.println(stu);System.out.println(user);String viewName = "success";ModelAndView modelAndView = new ModelAndView(viewName);modelAndView.addObject("user", user);modelAndView.addObject("student", stu);return modelAndView;}}

在这里插入图片描述
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);// 将当前Controller中所有被@InitBinder注解修饰的方法都获取到

在这里插入图片描述
在这里插入图片描述
ControllerAdviceController该类下面的 @InitBinder注解修饰的方法也被取到,SpringMVC源码-@ControllerAdvice和 @InitBinder注解源码讲解 @ControllerAdvice注解修饰类相关教程在这里。
在这里插入图片描述
查找当前处理类中所有包含@InitBinder注解修饰的方法查找@ControllerAdive注解修饰的类中有哪些@initBinder修饰的方法
全局的和当前controller的@InitBinder注解修饰的方法都获取到了,放在这里。
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
在这里插入图片描述
查找当前处理类中所有包含@ModelAttribute注解修饰的方法查找@ControlerAdive注解修饰的类中有哪些@ModelAttribute修饰的方法
在这里插入图片描述
// 使用modelFactory将sessionAttributes和注释了@ModelAttribute的方法的参数设置到model中
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 根据配置对ignoreDefaultModelOnRedirect进行设置
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
在这里插入图片描述
invocableMethod.invokeAndHandle(webRequest, mavContainer);开始执行方法调用
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
参数解析:
在这里插入图片描述

java">getParameterValues:427, RequestFacade (org.apache.catalina.connector)
getParameterValues:153, ServletWebRequest (org.springframework.web.context.request)
resolveName:183, RequestParamMethodArgumentResolver (org.springframework.web.method.annotation)
resolveArgument:117, AbstractNamedValueMethodArgumentResolver (org.springframework.web.method.annotation)
resolveArgument:123, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
getMethodArgumentValues:177, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:138, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:109, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:929, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:825, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:90, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1127, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

在这里插入图片描述
当前controller以及全局的ControllerAdvice修饰的类中有@InitBinder注解修饰的方法,所以后续要进入该方法
在这里插入图片描述
对解析出来的参数进行处理:
在这里插入图片描述
方法调用堆栈路径是:

java">initBinder:30, InitBinderController (com.mashibing.controller.initBinder)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:202, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:143, InvocableHandlerMethod (org.springframework.web.method.support)
initBinder:73, InitBinderDataBinderFactory (org.springframework.web.method.annotation)
createBinder:62, DefaultDataBinderFactory (org.springframework.web.bind.support)
resolveArgument:136, AbstractNamedValueMethodArgumentResolver (org.springframework.web.method.annotation)
resolveArgument:123, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
getMethodArgumentValues:177, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:138, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:109, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:929, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:825, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:90, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1127, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

在这里插入图片描述
所有 @InitBinder注解修饰的方法都进行遍历,除了当前controller的也包括全局的即ControllerAdvice修饰的类中。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

开始执行http://localhost:8080/spring_mymvc/param?id=1&name=zhangsana&data=2024-10-11 19:40:25 controller这个方法,链路如下:

java">getFormatDate:39, InitBinderController (com.mashibing.controller.initBinder)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:202, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:143, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:109, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:929, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:825, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:90, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1127, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

在这里插入图片描述
返回map去找对应的视图处理,
在这里插入图片描述
controller方法到这里就执行结束了,也有返回值

java">setResponseStatus:149, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeAndHandle:111, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:929, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:825, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:90, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1127, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

returnValue对应的是controller里的返回值,接下来开始状态码的赋值操作:
ServletInvocableHandlerMethod

java">/*** Set the response status according to the {@link ResponseStatus} annotation.*/private void setResponseStatus(ServletWebRequest webRequest) throws IOException {// 获得状态码,和 @ResponseStatus 注解相关HttpStatus status = getResponseStatus();if (status == null) {return;}// 设置响应的状态码HttpServletResponse response = webRequest.getResponse();if (response != null) {String reason = getResponseStatusReason();// 有 reason ,则设置 status + reasonif (StringUtils.hasText(reason)) {response.sendError(status.value(), reason);}else {// 无 reason ,则仅设置 statusresponse.setStatus(status.value());}}// To be picked up by RedirectView// 为了 RedirectView ,所以进行设置webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);}

getResponseStatus方法是调用HandlerMethod的,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这是ResponseStatus的赋值流程。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
从15个返回值处理器查找符合条件的进行返回,
在这里插入图片描述
开始执行:handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
在这里插入图片描述
重定向用flashMap保证数据不丢失
设置视图名,并且如果是重定向再进行额外的处理。
在这里插入图片描述

至此返回值处理器完成,开始获取ModelAndView对象

java">invokeHandlerMethod:937, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:825, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:90, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1127, DispatcherServlet (org.springframework.web.servlet)
doService:1006, DispatcherServlet (org.springframework.web.servlet)
processRequest:1085, FrameworkServlet (org.springframework.web.servlet)
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
得到mav对象
在这里插入图片描述

在这里插入图片描述
流程图如下:
在这里插入图片描述
mav返回视图之后开始后续执行,
在这里插入图片描述
包括设置视图名称,执行拦截器后置处理,渲染视图等。

java">// 执行响应的interceptor的postHandler方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
java">	/*** 	 从这里就可以看到 是倒序遍历拦截器数组 所以 postHandle的执行顺序刚好和定义的顺序相反,* Apply postHandle methods of registered interceptors.*/void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {//  获得拦截器数组HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {// 遍历拦截器数组,倒序执行for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];// 后置处理interceptor.postHandle(request, response, this.handler, mv);}}}

在这里插入图片描述
处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion

java">	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
java">/*** Handle the result of handler selection and handler invocation, which is* either a ModelAndView or an Exception to be resolved to a ModelAndView.*/private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {// 标记是否为处理生成异常的ModelAndView对象boolean errorView = false;// 如果请求处理过程中有异常抛出则处理异常if (exception != null) {// 从ModelAndViewDefiningException中获得ModelAndView对象if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}// 处理异常,生成ModelAndView对象else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?// 是否进行页面渲染if (mv != null && !mv.wasCleared()) {// 渲染页面render(mv, request, response);// 清理请求中的错误消息属性// 因为上述的情况中,processHandlerException会通过WebUtils设置错误消息属性,所以需要进行清理if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}// 如果启动了异步处理则返回if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}// 发出请求处理完成通知,触发Interceptor的afterCompletionif (mappedHandler != null) {// Exception (if any) is already handled..mappedHandler.triggerAfterCompletion(request, response, null);}}

开始渲染页面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

java">	protected View createView(String viewName, Locale locale) throws Exception {return loadView(viewName, locale);//UrlBasedViewResolver}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

java">	@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.// 往请求中设置一些属性,Locale、TimeZone、LocalizationContextexposeHelpers(request);// Determine the path for the request dispatcher.// 获取需要转发的路径String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).// 获取请求转发器RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}// 最后进行转发rd.forward(request, response);}}

页面渲染结果:
在这里插入图片描述


http://www.ppmy.cn/ops/126920.html

相关文章

STM32-USART串口协议

一、USART与UART 1、区别 同步通信‌&#xff1a;USART在同步通信时需要时钟来触发数据传输&#xff0c;能够提供主动时钟&#xff0c;这使得通信双方可以共享一个时钟信号来采样数据线。‌异步通信‌&#xff1a;在异步通信中&#xff0c;USART与UART没有区别&#xff0c;因…

如何在windows下搭建一个gitlab

在Windows上创建一个GitLab实例&#xff0c;最常用的方法是通过Docker来运行GitLab的官方镜像。以下是具体步骤&#xff1a; 前提条件 Windows系统&#xff1a;确保系统已经安装了Windows 10或更高版本。安装Docker Desktop&#xff1a;可以从 Docker官网 下载并安装。安装完…

【JS】哈希(数组)解决赎金信问题

思路 本文采用数组方式&#xff0c;创建长度为26的数组&#xff0c;用字母对应的Unicode编码做索引&#xff0c;先将杂志字符串每个字母存储到对应位置&#xff0c;再遍历赎金信字符串&#xff0c;减去数组中对应字母的计数&#xff0c;如果出现计数不够减的情况&#xff0c;返…

Docker学习笔记(3) - Docker命令

1. 帮助命令 docker version # 显示docker版本信息 docker info # 显示docker系统信息&#xff0c;包括镜像和容器 docker 命令 --help # 帮助命令2. 镜像命令 docker images # 查看本地主机上的镜像# 搜索镜像 docker search 镜像名称 # 搜索镜像 #…

对象的增删改查,数组api,字符串api,Date对象api,Math对象api

一、对象 1.什么是对象&#xff1f; 在js&#xff0c;对象是一组拥有无序的 属性和 方法的集合 生活中&#xff0c;万事万物皆对象&#xff0c;对象中可以抽象处两个概念&#xff1a;特征(属性)和行为&#xff08;方法&#xff09; 人&#xff1a;特征有姓名性别年龄身高体重…

英伟达CEO黄仁勋在BG2播客上做客

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

九、SQL 进阶之路:深入探索数据库查询的艺术

SQL 进阶之路&#xff1a;深入探索数据库查询的艺术 在当今这个数据驱动的时代&#xff0c;数据已经成为了各个行业和领域中至关重要的资源。在这样的大背景下&#xff0c;掌握 SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;已然成为了数据…

初学结构化查询语言的操作

SQL: Structured Query Language 结构化查询语言(Structured Query Language)简称SQL&#xff0c;是一种特殊目的的编程语言&#xff0c;是一种数 据库查询和程序设计语言&#xff0c;用于存取数据以及查询、更新和管理关系数据库系统。 一&#xff0c;SQL的分类 1&#xff…