Skip to content

Latest commit

 

History

History
1358 lines (966 loc) · 50.9 KB

10.第10章-SpringMVC中的HandlerAdapter源码解析.md

File metadata and controls

1358 lines (966 loc) · 50.9 KB

第10章-SpringMVC中的HandlerAdapter源码解析

测试类

HelloController

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

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  $END$
  结果: <h1 style="color: red">${sessionScope.msg}</h1>
  </body>
</html>

其余的类和前面一样,不列举了。

BeanNameUrlHandlerMapping简介

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

   /**
    * Checks name and aliases of the given bean for URLs, starting with "/".
    */
   @Override  //决定使用哪个url注册
   protected String[] determineUrlsForHandler(String beanName) {
      List<String> urls = new ArrayList<>();
      if (beanName.startsWith("/")) {
         urls.add(beanName);
      }
      String[] aliases = obtainApplicationContext().getAliases(beanName);
      for (String alias : aliases) {
         if (alias.startsWith("/")) {
            urls.add(alias);
         }
      }
      return StringUtils.toStringArray(urls);
   }

}
  1. 很简单,意思就是只要你的BeanName是以/开头,就会将这个Bean封装成一个BeanNameUrlHandlerMapping映射。
  2. 具体路径怎么写呢?你可以在你的类上写一个@Controller("/helloReq") ,这样就有路径了。代表的意思就是/helloReq这个URL和由哪个handler来处理的映射关系被保存在了BeanNameUrlHandlerMapping里
  3. 如果不懂的话可以待会看下面的例子

handler就是咱们常说的XXXController

HandlerAdapter概述

  1. 上面我们从HandlerMapping这个映射中心找到了由哪个Controller执行哪个方法
  2. 按照我们最简单的想法就是直接method.invoke()反射执行
  3. 但是实际上我们要考虑
    1. 是哪个对象执行方法?
    2. 方法里的参数有几个?
    3. 参数类型是什么?参数类型大概率有我们自己写的类,怎么处理
    4. 多个参数我们还要一个一个赋值。
    5. 怎么返回?是直接返回值,还是跳转页面,等等
  4. 其实是比较复杂的,springmvc就写了一个HandlerAdapter来处理这些复杂的逻辑

还是跟HandlerMapping一样,继续Debug DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)

浏览器输入:http://localhost:8080/springmvc_source_test/hello?name=zhangsan&user=haha开始测试

DispatcherServlet#doDispatch()请求派发

    //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()

    @Override //想让他工作,实现HttpRequestHandler接口即可
    public boolean supports(Object handler) { //写一个HttpRequestHandler的实现也能处理请求
       return (handler instanceof HttpRequestHandler);
    }

这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口,是的话就直接返回这个Adapter,不往下走了

SimpleControllerHandlerAdapter#supports()

    @Override
    public boolean supports(Object handler) {
       return (handler instanceof Controller);
    }

同理,这里判断当前handler是否实现了Controller接口,是的话就直接返回这个Adapter,不往下走了

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter自己没有写supports方法,它用的是父类的AbstractHandlerMethodAdapter的

AbstractHandlerMethodAdapter#supports()

    public final boolean supports(Object handler) {
       return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

这个就是判断当前handler是不是HandlerMethod类型的,前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod

RequestMappingHandlerAdapter#supportsInternal()

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

DispatcherServlet#doDispatch()

举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动

测试类-HelloHttpRequestHandler

@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

//@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()

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   return handleInternal(request, response, (HandlerMethod) handler);
}

AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,之前说过

RequestMappingHandlerAdapter#handleInternal()

    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( )

    /**
     * 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?

public String sayHello(Person person){ 
//...
}
  1. 假设方法里有一个Person类型的参数,Person里的属性就是age,name,sex这些的
  2. 前端传的参数名刚好能和Person的属性对上,WebDataBinder就会帮我们自动绑定上。

argumentResolvers参数解析器

注意有一些解析器,比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器

returnValueHandlers返回值解析器

15个参数解析器和27个返回值解析器是什么时候有值的?

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()

    public void afterPropertiesSet() { //初始化以后
       // Do this first, it may add ResponseBody advice beans
       initControllerAdviceCache();  //初始化 ControllerAdvice 【异常处理相关的功能】

       if (this.argumentResolvers == null) {  //拿到底层所有的默认 argumentResolvers
          List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
          this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); //把这些resolver统一组合到一个对象里面,方便管控
       }
       if (this.initBinderArgumentResolvers == null) {
          List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
          this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
       }
       if (this.returnValueHandlers == null) {
          List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
          this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
       }
    }

RequestMappingHandlerAdapter#getDefaultArgumentResolvers()

    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
       List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

       // Annotation-based argument resolution
       //这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
       resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
       resolvers.add(new RequestParamMapMethodArgumentResolver());
       resolvers.add(new PathVariableMethodArgumentResolver());
       resolvers.add(new PathVariableMapMethodArgumentResolver());
       resolvers.add(new MatrixVariableMethodArgumentResolver());
       resolvers.add(new MatrixVariableMapMethodArgumentResolver());
       resolvers.add(new ServletModelAttributeMethodProcessor(false));
       resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
       resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
       resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
       resolvers.add(new RequestHeaderMapMethodArgumentResolver());
       resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
       resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
       resolvers.add(new SessionAttributeMethodArgumentResolver());
       resolvers.add(new RequestAttributeMethodArgumentResolver());

       // Type-based argument resolution
       resolvers.add(new ServletRequestMethodArgumentResolver());
       resolvers.add(new ServletResponseMethodArgumentResolver());
       resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
       resolvers.add(new RedirectAttributesMethodArgumentResolver());
       resolvers.add(new ModelMethodProcessor());
       resolvers.add(new MapMethodProcessor());
       resolvers.add(new ErrorsMethodArgumentResolver());
       resolvers.add(new SessionStatusMethodArgumentResolver());
       resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
       if (KotlinDetector.isKotlinPresent()) {
          resolvers.add(new ContinuationHandlerMethodArgumentResolver());
       }

       // Custom arguments
       if (getCustomArgumentResolvers() != null) {
          resolvers.addAll(getCustomArgumentResolvers());
       }

       // Catch-all
       resolvers.add(new PrincipalMethodArgumentResolver());
       //这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
       resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
       resolvers.add(new ServletModelAttributeMethodProcessor(true));

       return resolvers;
    }

RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()

    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
       List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

       // Single-purpose return value types
       handlers.add(new ModelAndViewMethodReturnValueHandler());
       handlers.add(new ModelMethodProcessor());
       handlers.add(new ViewMethodReturnValueHandler());
       handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
             this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
       handlers.add(new StreamingResponseBodyReturnValueHandler());
       handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
             this.contentNegotiationManager, this.requestResponseBodyAdvice));
       handlers.add(new HttpHeadersReturnValueHandler());
       handlers.add(new CallableMethodReturnValueHandler());
       handlers.add(new DeferredResultMethodReturnValueHandler());
       handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

       // Annotation-based return value types
       handlers.add(new ServletModelAttributeMethodProcessor(false));
       handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
             this.contentNegotiationManager, this.requestResponseBodyAdvice));

       // Multi-purpose return value types
       handlers.add(new ViewNameMethodReturnValueHandler());
       handlers.add(new MapMethodProcessor());

       // Custom return value types
       if (getCustomReturnValueHandlers() != null) {
          handlers.addAll(getCustomReturnValueHandlers());
       }

       // Catch-all
       if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
          handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
       }
       else {
          handlers.add(new ServletModelAttributeMethodProcessor(true));
       }

       return handlers;
    }

RequestMappingHandlerAdapter中的参数解析器工作流程

DispatcherServlet#doDispatch()

AbstractHandlerMethodAdapter#handle(HttpServletRequest , HttpServletResponse , Object )RequestMappingHandlerAdapter#handleInternal(HttpServletRequest , HttpServletResponse , HandlerMethod )上面说过,跳过

RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法

ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法

    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( )目标方法的反射执行过程

    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()获取方法的请求参数

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()

    public boolean supportsParameter(MethodParameter parameter) {
       return getArgumentResolver(parameter) != null;
    }

HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数

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注解的参数解析器

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往参数解析器里加了两个
 	 //这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
       resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
       
        //这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
       resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
  1. 这里的true和false指定的就是useDefaultResolution这个东西

  2. 然后我们验证一下第二个RequestParamMethodArgumentResolver

  1. useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候,就用这个默认中的默认处理器第26个RequestParamMethodArgumentResolver(双重默认)
  2. 那什么时候用第27个ServletModelAttributeMethodProcessor这个处理器呢?当第26个在上面那几个if else中返回false,就会用最后这个,至于什么时候返回false,我没有深入研究。

RequestParamMapMethodArgumentResolver参数解析器

@RequestParam Map<String,Object> params

public boolean supportsParameter(MethodParameter parameter) {
   RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
   return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
         !StringUtils.hasText(requestParam.name()));
}

判断有没有标@RequestParam注解,并且类型是Map

PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器

    public boolean supportsParameter(MethodParameter parameter) {
       if (!parameter.hasParameterAnnotation(PathVariable.class)) {
          return false;
       }
       if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
          PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
          return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
       }
       return true;
    }

相信大家已经能猜到了,这里就是判断参数上有没有标注@PathVariable注解

返回到InvocableHandlerMethod#getMethodArgumentValues( )

具体的参数赋值过程,就是一些数据类型的转换,可以自己去看下。

args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

返回到InvocableHandlerMethod#invokeForRequest()

接下来就是交给反射去执行

返回到ServletInvocableHandlerMethod#invokeAndHandle()

拿到返回值了,接下来交给返回值处理器

SpringMVC到底能写哪些参数?

详见官方文档-参数相关

RequestMappingHandlerAdapter中的返回值解析器工作流程

@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<String,Object> params:所有请求参数全封装进来
		// @RequestHeader("User-Agent") String  ua 获取指定请求头的值
		String header = request.getHeader("User-Agent");
		//方法的签名,到底能写那些?
		//详细参照 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments
		//https://www.bilibili.com/video/BV19K4y1L7MT?p=32
		String mvc = helloService.say(user + ":MVC" + name);
		session.setAttribute("msg", mvc);


		//SpringMVC的目标方法能写哪些返回值
		//https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
		return "index.jsp";
	}
}

ServletInvocableHandlerMethod#invokeAndHandle()

我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高,接下来我们细讲

HandlerMethodReturnValueHandlerComposite#handleReturnValue()

    @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个返回值处理器找到合适的返回值处理器

    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()

    public boolean supportsReturnType(MethodParameter returnType) {
       Class<?> paramType = returnType.getParameterType();
       //返回值是void,或者字符串
       return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
    }

返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()

ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值

    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()进行视图解析相关工作

    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<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
          HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
          if (request != null) { //重定向数据的共享,RedirectView。先把数据移到request,再把request移到session
             RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
          }
       }
       return mav;
    }

注意如果你返回值写index而不是index.jsp,它会给你报404。意思就是它不会给我们加jsp后缀。

返回到DispatcherServlet#doDispatch()

DispatcherServlet#applyDefaultViewName()

    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()处理返回值

    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

    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()

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {

   if (this.viewResolvers != null) {
      //一样的for循环视图解析器
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}

不过默认的视图解析器只有一个

这里没有拼接前缀和后缀,所以我们必须要自己写.jsp或者.html

RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器

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

由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。