From 6e2d49803a3654472e482f270883f5f297925a30 Mon Sep 17 00:00:00 2001 From: youthlql <1826692270@qq.com> Date: Wed, 5 Oct 2022 19:16:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0Spring=E6=BA=90=E7=A0=81-spri?= =?UTF-8?q?ngmvc=20handlerAdapter=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- ...10章-SpringMVC中的HandlerAdapter源码解析.md | 1358 +++++++++++++++++ 2 files changed, 1361 insertions(+), 1 deletion(-) create mode 100644 docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md diff --git a/README.md b/README.md index a4a8d0c..201b9bd 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ -## 源码【06.21更新】 +## 源码 @@ -151,6 +151,8 @@ [09.第9章-SpringMVC请求处理源码和HandlerMapping原理](docs/spring-sourcecode-v1/09.第9章-SpringMVC请求处理源码和HandlerMapping原理.md) +[10.第10章-SpringMVC中的HandlerAdapter源码解析](docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md) + # Netty ## 入门 diff --git a/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md b/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md new file mode 100644 index 0000000..2bb655c --- /dev/null +++ b/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md @@ -0,0 +1,1358 @@ +# 第10章-SpringMVC中的HandlerAdapter源码解析 + +## 测试类 + +### HelloController + +```java +package cn.imlql.web.controller; + +@Controller +public class HelloController { + + public HelloController() { + System.out.println("HelloController....."); + } + + @Autowired + HelloService helloService; + + @GetMapping("/hello") + public String sayHello(String name, + @RequestParam("user") String user, + HttpSession session) { + String mvc = helloService.say(user + ":MVC" + name); + session.setAttribute("msg", mvc); + return "index.jsp"; + } +} + +``` + +### index.jsp + +```jsp +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + +
+
+
+
+
+```java
+ //SpringMVC处理请求的核心流程
+ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ HttpServletRequest processedRequest = request;
+ HandlerExecutionChain mappedHandler = null; //handler(目标方法)的执行链
+ boolean multipartRequestParsed = false; //文件上传标志
+ //对异步请求的支持(Servlet3.0以后才有的,Webflux)
+ WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
+
+ try {
+ ModelAndView mv = null;
+ Exception dispatchException = null;
+
+ try {
+ processedRequest = checkMultipart(request); //检查当前是否文件上传请求
+ multipartRequestParsed = (processedRequest != request);
+
+ //构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request.
+ mappedHandler = getHandler(processedRequest);
+ if (mappedHandler == null) { //如果找不到人处理,就send 404
+ noHandlerFound(processedRequest, response);
+ return;
+ }
+
+ //适配器怎么找的、 Determine handler adapter for the current request.
+ HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
+
+ // Process last-modified header, if supported by the handler.
+ String method = request.getMethod();
+ boolean isGet = "GET".equals(method);
+ if (isGet || "HEAD".equals(method)) {
+ long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
+ if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
+ return;
+ }
+ }
+ //所有拦截器的 preHandle 执行
+ if (!mappedHandler.applyPreHandle(processedRequest, response)) {
+ return; //使用 mappedHandler整个链
+ }
+
+ //真正来执行目标方法 Actually invoke the handler.
+ 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);
+ }
+ }
+ }
+ }
+```
+
+
+
+F7进入`getHandlerAdapter(mappedHandler.getHandler())`
+
+
+
+
+
+### DispatcherServlet#getHandlerAdapter()
+
+
+
+上面几个默认的适配器还是在`DispatcherServlet.properties`配置的4个默认适配器,然后在初始化九大组件的时候放到容器里
+
+
+
+### HttpRequestHandlerAdapter#supports()
+
+```java
+ @Override //想让他工作,实现HttpRequestHandler接口即可
+ public boolean supports(Object handler) { //写一个HttpRequestHandler的实现也能处理请求
+ return (handler instanceof HttpRequestHandler);
+ }
+```
+
+这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口,是的话就直接返回这个Adapter,不往下走了
+
+### SimpleControllerHandlerAdapter#supports()
+
+```java
+ @Override
+ public boolean supports(Object handler) {
+ return (handler instanceof Controller);
+ }
+```
+
+同理,这里判断当前handler是否实现了Controller接口,是的话就直接返回这个Adapter,不往下走了
+
+### RequestMappingHandlerAdapter
+
+RequestMappingHandlerAdapter自己没有写`supports`方法,它用的是父类的AbstractHandlerMethodAdapter的
+
+#### AbstractHandlerMethodAdapter#supports()
+
+```java
+ public final boolean supports(Object handler) {
+ return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
+ }
+```
+
+这个就是判断当前handler是不是HandlerMethod类型的,前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod
+
+
+
+#### RequestMappingHandlerAdapter#supportsInternal()
+
+```java
+ protected boolean supportsInternal(HandlerMethod handlerMethod) {
+ return true;
+ }
+```
+
+### DispatcherServlet#doDispatch()
+
+
+
+## 举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动
+
+### 测试类-HelloHttpRequestHandler
+
+```java
+@Controller("/helloReq") //BeanNameUrlHandlerMapping 就会把他注册进去
+public class HelloHttpRequestHandler implements HttpRequestHandler {
+ //启用 HttpRequestHandlerAdapter
+
+ //处理请求
+ @Override
+ public void handleRequest(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+ response.getWriter().write("HelloHttpRequestHandler....");
+ }
+}
+```
+
+`@Controller("/helloReq")`里的/helloReq即是BeanName也是URL。这里就是之前说的BeanName以/为开头的Bean,它的URL和handler的映射关系会被保存在BeanNameUrlHandlerMapping里,如下图
+
+
+
+
+
+### DispatcherServlet#getHandlerAdapter()
+
+
+
+因为我们的HelloHttpRequestHandler正好实现了HttpRequestHandler,所以就会直接返回HttpRequestHandlerAdapter
+
+
+
+
+
+### DispatcherServlet#doDispatch()
+
+我们直接来到准备执行的地方
+
+
+
+F7进入`ha.handle(processedRequest, response, mappedHandler.getHandler())`
+
+### HttpRequestHandlerAdapter#handle()
+
+
+
+直接来到了这里,接着就调用我们自定义的HelloHttpRequestHandler的handleRequest方法。
+
+
+
+
+
+### 测试类-HelloSimpleController
+
+```java
+//@RequestMapping("/ssss")
+@Controller("/helloSimple")
+public class HelloSimpleController implements Controller {
+ @Override
+ public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ return null;
+ }
+}
+```
+
+实现Controller接口的就是SimpleControllerHandlerAdapter来处理,原理和HttpRequestHandlerAdapter几乎一样。
+
+
+
+
+
+### 小总结
+
+1. 很显然BeanNameUrlHandlerMapping和HttpRequestHandlerAdapter的结合,一个类只能处理一个URL路径的请求。并且这里的参数处理都很麻烦,不像@RequestMapping那么方便
+2. 远不如RequestMappingHandlerMapping和RequestMappingHandlerAdapter的结合来的方便。它两结合后,一个类里可以写多个@RequestMapping注解标注的方法,一个类里就可以处理多个URL请求,并且处理参数和返回值都很方便。
+3. 所以下面就重点讲RequestMappingHandlerAdapter
+
+
+
+
+
+## RequestMappingHandlerAdapter中的参数解析器、返回值处理器=>概述
+
+### DispatcherServlet#doDispatch()
+
+
+
+
+
+
+
+
+
+### AbstractHandlerMethodAdapter#handle()
+
+```java
+public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+
+ return handleInternal(request, response, (HandlerMethod) handler);
+}
+```
+
+
+
+AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,之前说过
+
+
+
+### RequestMappingHandlerAdapter#handleInternal()
+
+```java
+ 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;
+ }
+
+ //session锁这个东西不适用于高并发场景,所以spring默认是不适用它,应该是可以开启的,具体怎么开启我没研究,可以看官方文档
+ private boolean synchronizeOnSession = false;
+```
+
+
+
+
+
+
+
+
+
+### RequestMappingHandlerAdapter#invokeHandlerMethod( )
+
+```java
+ /**
+ * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
+ * if view resolution is required.
+ * @since 4.2
+ * @see #createInvocableHandlerMethod(HandlerMethod)
+ */
+ @Nullable
+ protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
+ HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+ //把原生的request,response封装到一个对象中方便后续只用这一个参数就行【装饰器模式】
+ ServletWebRequest webRequest = new ServletWebRequest(request, response);
+ try { //数据绑定器
+ WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
+ //获取到模型工厂 Model(要交给页面的数据) View(我们要去的 视图),视图可以理解为页面或者图片等等
+ ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
+
+ // 这里就是做了一些增强,比如我们可以直接通过ServletInvocableHandlerMethod拿到返回值,可以很方便的拿到其它我们需要的信息
+ ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
+ if (this.argumentResolvers != null) { //参数解析器
+ invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
+ }
+ if (this.returnValueHandlers != null) { //返回值解析器
+ invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
+ }
+ invocableMethod.setDataBinderFactory(binderFactory);
+ invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
+ //以上的 几个核心组件都挺重要的 ModelAndViewContainer是以后处理过程中产生的ModelAndView数据临时存储容器
+ // 也是整个请求处理流程,线程共享数据
+ ModelAndViewContainer mavContainer = new ModelAndViewContainer();
+ mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
+ modelFactory.initModel(webRequest, mavContainer, invocableMethod);
+ mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
+ //异步请求有关的
+ AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
+ asyncWebRequest.setTimeout(this.asyncRequestTimeout);
+
+ WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
+ asyncManager.setTaskExecutor(this.taskExecutor);
+ asyncManager.setAsyncWebRequest(asyncWebRequest);
+ asyncManager.registerCallableInterceptors(this.callableInterceptors);
+ asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
+
+ 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 + "]";
+ });
+ invocableMethod = invocableMethod.wrapConcurrentResult(result);
+ }
+ //真正开始执行目标方法
+ invocableMethod.invokeAndHandle(webRequest, mavContainer);
+ if (asyncManager.isConcurrentHandlingStarted()) {
+ return null;
+ }
+
+ return getModelAndView(mavContainer, modelFactory, webRequest);
+ }
+ finally {
+ webRequest.requestCompleted();
+ }
+ }
+```
+
+#### 什么是WebDataBinderFactory?
+
+```java
+public String sayHello(Person person){
+//...
+}
+```
+
+1. 假设方法里有一个Person类型的参数,Person里的属性就是age,name,sex这些的
+2. 前端传的参数名刚好能和Person的属性对上,WebDataBinder就会帮我们自动绑定上。
+
+#### argumentResolvers参数解析器
+
+
+
+
+
+注意有一些解析器,比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器
+
+#### returnValueHandlers返回值解析器
+
+
+
+
+
+### 15个参数解析器和27个返回值解析器是什么时候有值的?
+
+```java
+public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
+ implements BeanFactoryAware, InitializingBean {
+ // ...
+}
+```
+
+
+
+我们可以看到RequestMappingHandlerAdapter也是实现了InitializingBean接口,所以它什么时候有值,和RequestMappingHandlerMapping是一样的逻辑
+
+1. `DispatcherServlet#onRefresh()`开始初始化九大组件,就会开始初始化HandlerAdapter
+2. 首先是创建`DispatcherServlet.properties`里指定的四个HandlerAdapter实现类的对象。还是用createBean来创建HandlerAdapter的
+3. 其中RequestMappingHandlerAdapter创建完对象后,因为它实现了InitializingBean,所以会调用RequestMappingHandlerAdapter#afterPropertiesSet()
+4. 在afterPropertiesSet()中就直接new了所有默认的参数解析器和返回值解析器
+
+
+
+#### RequestMappingHandlerAdapter#afterPropertiesSet()
+
+```java
+ public void afterPropertiesSet() { //初始化以后
+ // Do this first, it may add ResponseBody advice beans
+ initControllerAdviceCache(); //初始化 ControllerAdvice 【异常处理相关的功能】
+
+ if (this.argumentResolvers == null) { //拿到底层所有的默认 argumentResolvers
+ List
+
+
+
+`AbstractHandlerMethodAdapter#handle(HttpServletRequest , HttpServletResponse , Object )`和`RequestMappingHandlerAdapter#handleInternal(HttpServletRequest , HttpServletResponse , HandlerMethod )`上面说过,跳过
+
+### RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法
+
+
+
+
+
+
+
+### ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法
+
+```java
+ public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
+ Object... providedArgs) throws Exception {
+ //目标方法的反射执行过程
+ Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
+ setResponseStatus(webRequest);
+
+ if (returnValue == null) {
+ if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
+ disableContentCachingIfNecessary(webRequest);
+ mavContainer.setRequestHandled(true);
+ return;
+ }
+ }
+ else if (StringUtils.hasText(getResponseStatusReason())) {
+ mavContainer.setRequestHandled(true);
+ return;
+ }
+
+ mavContainer.setRequestHandled(false);
+ Assert.state(this.returnValueHandlers != null, "No return value handlers");
+ try {
+ this.returnValueHandlers.handleReturnValue(
+ returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
+ }
+ catch (Exception ex) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(formatErrorForReturnValue(returnValue), ex);
+ }
+ throw ex;
+ }
+ }
+```
+
+
+
+### InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程
+
+```java
+ public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
+ Object... providedArgs) throws Exception {
+ //获取方法的请求参数
+ Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Arguments: " + Arrays.toString(args));
+ }
+ return doInvoke(args); //就是反射执行
+ }
+```
+
+
+
+开始进入正题
+
+### InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数
+
+```java
+protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
+ Object... providedArgs) throws Exception {
+ //直接拿到方法的所有参数,getMethodParameters是HandlerMethod类的,在之前构造handler的时候就已经通过反射将相关信息保存好了
+ // 我们之前讲过标注@RequestMapping注解的handler最终会被封装成HandlerMethod
+ MethodParameter[] parameters = getMethodParameters();
+ if (ObjectUtils.isEmpty(parameters)) {
+ return EMPTY_ARGS;
+ }
+ //准备args数组(和parameters一样长),挨个确定每个参数都是什么值
+ Object[] args = new Object[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ MethodParameter parameter = parameters[i];
+ parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
+ args[i] = findProvidedArgument(parameter, providedArgs); //先去已提供的参数中找
+ if (args[i] != null) {
+ continue;
+ }
+ if (!this.resolvers.supportsParameter(parameter)) { //支持这种参数的解析器也会被放到缓存,下一次进来,就不用27个人挨个判断
+ throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
+ }
+ try {
+ args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
+ }
+ catch (Exception ex) {
+ // Leave stack trace for later, exception may actually be resolved and handled...
+ if (logger.isDebugEnabled()) {
+ String exMsg = ex.getMessage();
+ if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
+ logger.debug(formatArgumentError(parameter, exMsg));
+ }
+ }
+ throw ex;
+ }
+ }
+ return args;
+}
+```
+
+
+
+### HandlerMethodArgumentResolverComposite准备循环27个参数解析器
+
+
+
+### HandlerMethodArgumentResolverComposite#supportsParameter()
+
+```java
+ public boolean supportsParameter(MethodParameter parameter) {
+ return getArgumentResolver(parameter) != null;
+ }
+```
+
+### HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数
+
+```java
+private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
+ HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); //先看缓存中有没有
+ if (result == null) {
+ for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
+ if (resolver.supportsParameter(parameter)) {
+ result = resolver;
+ this.argumentResolverCache.put(parameter, result);
+ break;
+ }
+ }
+ }
+ return result;
+}
+```
+
+这里就是循环27个参数解析器,看哪一个能解析这种类型的参数。因为参数解析器太多,我们就举几个常用的例子
+
+
+
+### RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器
+
+```java
+package org.springframework.web.method.annotation
+
+
+ public boolean supportsParameter(MethodParameter parameter) {
+ if (parameter.hasParameterAnnotation(RequestParam.class)) {
+ //注意这里是为了不跟下面的RequestParamMapMethodArgumentResolver产生冲突
+ if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
+ RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
+ return (requestParam != null && StringUtils.hasText(requestParam.name()));
+ }
+ else {
+ return true;
+ }
+ }
+ else {
+ if (parameter.hasParameterAnnotation(RequestPart.class)) {
+ return false;
+ }
+ parameter = parameter.nestedIfOptional();
+ if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
+ return true;
+ }
+ else if (this.useDefaultResolution) {
+ return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
+ }
+ else {
+ return false;
+ }
+ }
+ }
+```
+
+这个就是判断参数有没有标@RequestParam注解
+
+注意这个是org.springframework.web.method.annotation包下的
+
+
+
+
+
+1. 第一次进入`RequestParamMethodArgumentResolver#supportsParameter(MethodParameter parameter)`时,我们在前面讲过,在`RequestMappingHandlerAdapter#getDefaultArgumentResolvers()`添加默认解析器的时候,spring往参数解析器里加了两个
+
+```java
+ //这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
+ resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
+
+ //这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
+ resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
+```
+
+2. 这里的true和false指定的就是useDefaultResolution这个东西
+
+3. 然后我们验证一下第二个RequestParamMethodArgumentResolver
+
+
+
+4. useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候,就用这个默认中的默认处理器第26个`RequestParamMethodArgumentResolver`(双重默认)
+5. 那什么时候用第27个`ServletModelAttributeMethodProcessor`这个处理器呢?当第26个在上面那几个if else中返回false,就会用最后这个,至于什么时候返回false,我没有深入研究。
+
+
+
+### RequestParamMapMethodArgumentResolver参数解析器
+
+> `@RequestParam Map
+
+
+
+具体的参数赋值过程,就是一些数据类型的转换,可以自己去看下。
+
+```
+args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
+```
+
+
+
+
+
+### 返回到InvocableHandlerMethod#invokeForRequest()
+
+
+
+接下来就是交给反射去执行
+
+
+
+
+
+### 返回到ServletInvocableHandlerMethod#invokeAndHandle()
+
+
+
+拿到返回值了,接下来交给返回值处理器
+
+### SpringMVC到底能写哪些参数?
+
+详见[官方文档-参数相关](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments)
+
+
+
+
+
+
+
+## RequestMappingHandlerAdapter中的返回值解析器工作流程
+
+```java
+@Controller
+public class HelloController {
+
+ public HelloController() {
+ System.out.println("HelloController.....");
+ }
+
+ @Autowired
+ HelloService helloService;
+
+ @GetMapping("/hello")
+ public String sayHello(String name,
+ @RequestParam("user") String user,
+ HttpSession session, HttpServletRequest request, //原生的session对象
+ @RequestHeader("User-Agent") String ua) { //@RequestParam Map
+
+我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高,接下来我们细讲
+
+### HandlerMethodReturnValueHandlerComposite#handleReturnValue()
+
+```java
+ @Override
+ public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
+ ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
+ //找到合适的返回值处理器.
+ HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
+ if (handler == null) {
+ throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
+ } //返回值处理器. 处理返回值
+ handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
+ }
+```
+
+### HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器
+
+```java
+ private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
+ boolean isAsyncValue = isAsyncReturnValue(value, returnType);
+ //还是老样子for循环
+ for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
+ if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
+ continue;
+ }
+ if (handler.supportsReturnType(returnType)) {
+ return handler;
+ }
+ }
+ return null;
+ }
+```
+
+我们就直接放行,看最终找到的是哪个
+
+
+
+
+
+### ViewNameMethodReturnValueHandler#supportsReturnType()
+
+```java
+ public boolean supportsReturnType(MethodParameter returnType) {
+ Class> paramType = returnType.getParameterType();
+ //返回值是void,或者字符串
+ return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
+ }
+```
+
+
+
+### 返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()
+
+
+
+
+
+### ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值
+
+```java
+ public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
+ ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
+
+ if (returnValue instanceof CharSequence) { //只要是字符串就是跳转到的页面地址
+ String viewName = returnValue.toString();
+ mavContainer.setViewName(viewName);
+ if (isRedirectViewName(viewName)) { //是否是重定向的方式 redirect:
+ mavContainer.setRedirectModelScenario(true);
+ }
+ }
+ else if (returnValue != null) {
+ // should not happen
+ throw new UnsupportedOperationException("Unexpected return type: " +
+ returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
+ }
+ }
+```
+
+
+
+然后就准备返回了
+
+
+
+### 返回到RequestMappingHandlerAdapter#invokeHandlerMethod()
+
+
+
+### RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作
+
+```java
+ private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
+ ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
+ //modelFactory准备模型数据 (请求域数据共享)session里面的数据搬家到request域
+ modelFactory.updateModel(webRequest, mavContainer);
+ if (mavContainer.isRequestHandled()) {
+ return null;
+ }
+ ModelMap model = mavContainer.getModel();
+ ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
+ if (!mavContainer.isViewReference()) {
+ mav.setView((View) mavContainer.getView());
+ }
+ if (model instanceof RedirectAttributes) {
+ Map
+
+注意如果你返回值写index而不是index.jsp,它会给你报404。意思就是它不会给我们加jsp后缀。
+
+
+
+### 返回到DispatcherServlet#doDispatch()
+
+
+
+
+
+### DispatcherServlet#applyDefaultViewName()
+
+```java
+ private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
+ if (mv != null && !mv.hasView()) { //如果没有指定跳转的页面
+ String defaultViewName = getDefaultViewName(request); //给一个默认页面
+ if (defaultViewName != null) {
+ mv.setViewName(defaultViewName);
+ }
+ }
+ }
+
+
+ protected String getDefaultViewName(HttpServletRequest request) throws Exception {
+ return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
+ }
+
+ @Nullable //把请求转成视图名(我们要跳转的页面地址)的翻译器【没啥用】
+ private RequestToViewNameTranslator viewNameTranslator;
+```
+
+这里不是重点,我直接告诉你结果吧,默认页面就是把request的请求路径直接拿来当要去的页面地址 。
+
+比如你的请求路径是`@GetMapping("/hello.html")`,但是你返回值写的是void,那么它就会给你返回到hello.html页面
+
+
+
+
+
+### 返回到DispatcherServlet#doDispatch()
+
+
+
+来到拦截器的后置处理环节
+
+
+
+然后来到处理结果环节
+
+### springmvc能写哪些返回值
+
+官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
+
+## 视图解析器解析流程
+
+### DispatcherServlet#processDispatchResult()处理返回值
+
+```java
+ 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();//即使有异常,这里也会返回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? 为啥?@ResponseBody(提前在解析返回值的时候,就已经把数据写出去了,所以这一步就没有了)
+ if (mv != null && !mv.wasCleared()) {
+ render(mv, request, response); //渲染ModeAndView,来解析模型和视图;最终决定响应效果
+ 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 forward
+ return;
+ }
+
+ if (mappedHandler != null) {
+ // Exception (if any) is already handled..
+ mappedHandler.triggerAfterCompletion(request, response, null);
+ }
+ }
+```
+
+进入此方法
+
+
+
+
+
+### DispatcherServlet#render()渲染ModelAndView
+
+```java
+ protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
+ // Determine locale for request and apply it to the response.
+ Locale locale =
+ (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
+ response.setLocale(locale);
+
+ View view;
+ String viewName = mv.getViewName();
+ if (viewName != null) {
+ // We need to resolve the view name. 关键还是这里
+ view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
+ if (view == null) {
+ throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
+ "' in servlet with name '" + getServletName() + "'");
+ }
+ }
+ else {
+ // No need to lookup: the ModelAndView object contains the actual View object.
+ view = mv.getView();
+ if (view == null) {
+ throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
+ "View object in servlet with name '" + getServletName() + "'");
+ }
+ }
+
+ // Delegate to the View object for rendering.
+ if (logger.isTraceEnabled()) {
+ logger.trace("Rendering view [" + view + "] ");
+ }
+ try {
+ if (mv.getStatus() != null) {
+ response.setStatus(mv.getStatus().value());
+ }
+ view.render(mv.getModelInternal(), request, response);
+ }
+ catch (Exception ex) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Error rendering view [" + view + "]", ex);
+ }
+ throw ex;
+ }
+ }
+```
+
+
+
+### DispatcherServlet#resolveViewName()
+
+```java
+protected View resolveViewName(String viewName, @Nullable Map
+
+这里没有拼接前缀和后缀,所以我们必须要自己写.jsp或者.html
+
+### RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器
+
+```java
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ return parameter.hasParameterAnnotation(RequestBody.class);
+ }
+
+ @Override
+ public boolean supportsReturnType(MethodParameter returnType) {
+ return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
+ returnType.hasMethodAnnotation(ResponseBody.class));
+ }
+
+ public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
+
+ parameter = parameter.nestedIfOptional();
+ Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
+ String name = Conventions.getVariableNameForParameter(parameter);
+
+ if (binderFactory != null) {
+ WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
+ if (arg != null) {
+ validateIfApplicable(binder, parameter);
+ if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
+ throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
+ }
+ }
+ if (mavContainer != null) {
+ mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
+ }
+ }
+
+ return adaptArgumentIfNecessary(arg, parameter);
+ }
+
+ @Override //如果返回值标注了 @ResponseBody注解。会被这个人拦截处理器
+ public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
+ ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
+ throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
+
+ mavContainer.setRequestHandled(true);
+ ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
+ ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
+
+ // Try even with null return value. ResponseBodyAdvice could get involved.
+ writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
+ }
+```
+
+
+
+由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。
+
+
+
+
+
+
+
+
+