我们在上文知道了HandlerMapping的主要目的是用于查找handler,找到这样的一个处理器肯定是要拉出来处理什么东西的,所以怎么让他真正执行handler的职责,就是HandlerAdapter需要做的事。

之所以要使用 HandlerAdapter 是因为 Spring MVC 中并没有对处理器做任何限制, 处理器可以以任意合理的方式来表现, 可以是一个类, 也可以是一个方法, 还可以是别的合理的方式, 从 handle 方法可以看出它是 Object 的类型。 但是我们的一个请求进来就是固定的入参HttpServletRequestHttpServletResponse,所以适配器的模式可以让我们的处理更加灵活。

下图为HandlerAdapter的类关系图:

HandlerAdapter
HandlerAdapter

HandlerAdapter 有三个方法,一个用于判断是否支持传入handler,一个用来使用handler进行处理请求,还有一个用来获取资源的LastModified值。

HandlerFunctionAdapter是在Spring 5.2新增的一个适配器,适用于Spring WebFlux框架,这里暂时先pass它,在一般的ssm框架中暂时还用不到。其他三个HttpRequestHandlerAdapterSimpleControllerHandlerAdapterSimpleServletHandlerAdapter代码十分简单这里不做叙述,自己看下源码,handle方法几乎都只有一行。

我们主要关注AbstractHandlerMethodAdapterRequestMappingHandlerAdapter两个类。

AbstractHandlerMethodAdapter

这个 几乎没什么逻辑,暴露了几个抽象方法其他都交由子类RequestMappingHandlerAdapter实现,当前类只是做了一层简单的类型转换,将是Object类型的handler转成HandlerMethod,并且实现了Order接口,可以在配置时设置顺序。

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

protected abstract boolean supportsInternal(HandlerMethod handlerMethod);

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

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

@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
        
@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
    return getLastModifiedInternal(request, (HandlerMethod) handler);
}

protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);

RequestMappingHandlerAdapter

这个类就是最核心的实现部分了。

现在我们回到他的父类三个方法,看他在子类的实现情况:

首先supportsInternal方法,并没有多余的逻辑也就是只要满足父类的是HandlerMethod类型的handler即可!

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

再看getLastModifiedInternal,发现也是直接返回-1;

剩下最关键的handleInternal!几百行的代码都是为了这个方法!就是这个方法实际使用 Handler 处理请求。具体处理过程大致可以分为三步:

  1. 处理器所需要的参数的绑定;
  2. 使用处理器处理请求;
  3. 处理返回值, 也就是将不同类型的返回值统一处理成 ModelAndView 类型。

参数的准备工作也就是我们常说的参数绑定, 这一步根据处理器的需要设置参数,而参数的类型、个数都是不确定的,所以难度非常大,另外这个过程中使用了大量的组件,

要想理解参数的绑定需要先想明白三个问题:

  • 都有哪些参数需要绑定
  • 参数的值的来源
  • 具体进行绑定的方法

需要绑定的参数当然是根据方法确定了, 不过这里的方法除了实际处理请求的处理器之外还有两个方法的参数需要绑定, 那就是跟当前处理器相对应的@ModelAttribute@InitBinder 注解的方法。

参数来源有6个:

  • request中相关的参数, 主要包括url中的参数、post过来的参数以及请求头所包含的值;
  • cookie中的参数;
  • session中的参数;
  • 设置到FlashMap中的参数, 这种参数主要用于redirect的参数传递;
  • SessionAttributes传递的参数, 这类参数通过@SessionAttributes注释传递;
  • 通过相应的注释了@ModelAttribute的方法进行设置的参数。

参数具体解析是使用HandlerMethodArgumentResolver类型的组件完成的, 不同类型的参数使用不同的 ArgumentResolver来韶析。有的 Resolver内部使用了WebDataBinder, 可以通过注释了@InitBinder的方法来初始化。 使用@InitBinder注解的方法也需要绑定参数, 而且也是不确定的, 所以@lnitBinder注释的方法本身也需要ArgumentResolver来解析参数, 但它使用的和Handler使用的不是同一套ArgumentResolver。 另外,注释了@ModelAttribute的方法也需要绑定参数, 它使用的和Handler使用的是同一套ArgumentResolver。

构造函数

bean的构造函数先执行再执行afterPropertiesSet,所以我们先看一下RequestMappingHandlerAdapter这个bean被注入的地方:

//WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
      @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
      @Qualifier("mvcConversionService") FormattingConversionService conversionService,
      @Qualifier("mvcValidator") Validator validator) {

   RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
   adapter.setContentNegotiationManager(contentNegotiationManager);
   adapter.setMessageConverters(getMessageConverters());
   adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
   adapter.setCustomArgumentResolvers(getArgumentResolvers());
   adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

   if (jackson2Present) {
      adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
      adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
   }

   AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
   configureAsyncSupport(configurer);
   if (configurer.getTaskExecutor() != null) {
      adapter.setTaskExecutor(configurer.getTaskExecutor());
   }
   if (configurer.getTimeout() != null) {
      adapter.setAsyncRequestTimeout(configurer.getTimeout());
   }
   adapter.setCallableInterceptors(configurer.getCallableInterceptors());
   adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

   return adapter;
}

如果你使用了默认的@EnableWebMvc+实现WebMvcConfigurer实现自定义的方式注入配置,那么Spring就会为你注入这个bean。

再进一步看这个构造函数里有没有做什么事情吧:

public RequestMappingHandlerAdapter() {
   this.messageConverters = new ArrayList<>(4);
   this.messageConverters.add(new ByteArrayHttpMessageConverter());
   this.messageConverters.add(new StringHttpMessageConverter());
   try {
      this.messageConverters.add(new SourceHttpMessageConverter<>());
   }
   catch (Error err) {
      // Ignore when no TransformerFactory implementation is available
   }
   this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

我们可以发现这里注册了一些messageConverter,其中AllEncompassingFormHttpMessageConverter是一个组合体,用于支持xml/json

public AllEncompassingFormHttpMessageConverter() {
   try {
      addPartConverter(new SourceHttpMessageConverter<>());
   }
   catch (Error err) {
      // Ignore when no TransformerFactory implementation is available
   }

   if (jaxb2Present && !jackson2XmlPresent) {
      addPartConverter(new Jaxb2RootElementHttpMessageConverter());
   }

   if (jackson2Present) {
      addPartConverter(new MappingJackson2HttpMessageConverter());
   }
   else if (gsonPresent) {
      addPartConverter(new GsonHttpMessageConverter());
   }
   else if (jsonbPresent) {
      addPartConverter(new JsonbHttpMessageConverter());
   }

   if (jackson2XmlPresent) {
      addPartConverter(new MappingJackson2XmlHttpMessageConverter());
   }

   if (jackson2SmilePresent) {
      addPartConverter(new MappingJackson2SmileHttpMessageConverter());
   }
}

哈,似乎看到了一个眼熟的货色MappingJackson2HttpMessageConverter!我们在配置中也与它打过交道,只知道它能够将对象转成json,没想到在这里被加载进RequestMappingHandlerAdapter,假如你看过《Spring 实战(第四版)》你会注意到提到HttpMessageConverter,书中会有一张表格列出一系列的信息转换器,其中部分是Spring自动注册的,这里我们也看到了AllEncompassingFormHttpMessageConverter以及它的父类FormHttpMessageConverter会注册一部分转换器。我们后面再看看他究竟怎么发挥作用的吧!

初始化

RequestMappingHandlerAdapter的创建在afterPropertiesSet方法中实现, 其内容主要是初始化 了argumentResolversinitBinderArgumentResolversreturnValueHandlers以及@ControllerAdvice 注解的类相关的modelAttributeAdviceCacheinitBinderAdviceCacheresponseBodyAdvice这6个属性, 下面分别介绍这6个属性

  • modelAttributeAdviceCacheinitBinderAdviceCache: 分别用于缓存@ControllerAdvice
  • 注解的类里面使用@ModelAttribute@InitBinder注解的方法,也就是全局的@ModelAttribute@InitBinder方法。 每个 处理器自己的@ModelAttribute@lnitBinder方法是在第一次使用处理器处理请求时缓存起来的,这种做法既不需要启动时就花时间遍历每个Controller查找@ModelAttribute@lnitBinder方法, 又能在调用过一次后再调用相同处理器处理请求时不需要再次查找而从缓存中获取。
  • requestResponseBodyAdvice : 用来保存前面介绍过的实现了ResponseBodyAdvice/RequestBodyAdvice接口,可以修改返回的 ResponseBody/RequestBody的类。
  • argumentResolvers: 用于给处理器方法和使用@ModelAttribute注解的方法设置参数,在初始化会注册一批默认的Resolver,并且吸纳CustomArgumentResolvers。
  • initBinderArgurnentResolvers: 用于给使用@initBinder的方法设置参数。在初始化会注册一批默认的Resolver,并且吸纳CustomArgumentResolvers
  • returnValueHandlers: 用于将处理器的返回值处理成ModelAndView的类型,在初始化会注册一批默认的Handler,并且吸纳customReturnValueHandlers
@Override
public void afterPropertiesSet() {
   // 初始化ControllerAdvice注解相关的属性
   initControllerAdviceCache();

   if (this.argumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
      this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   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);
   }
}

可以看到这里初始化分4步, 首先初始化ControllerAdvice注解相关的三个属性, 然后依次初始化argumentResolversinitBinderArgumentResolversreturn­ValueHandlers。 后面三个属性初始化的方式都一样, 都是先调用getDefaultXXX得到相应的值,然后设置给对应的属性, 而且都是new 出来的XXXComposite 类型, 这种类型在HandlerMapping 中的 RequestCondition 时已经见到过了,使用的是责任链模式,它自己并不实际干活,而是封装了多个别的组件, 干活时交给别的组件,主要作用是方便调用。

现在开始看initControllerAdviceCache()

private void initControllerAdviceCache() {
   if (getApplicationContext() == null) {
      return;
   }
//拿到所有的被`@ControllerAdvice`注解修饰的bean,并根据order排序
   List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

   List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

   for (ControllerAdviceBean adviceBean : adviceBeans) {
      Class<?> beanType = adviceBean.getBeanType();
      if (beanType == null) {
         throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
      }
      //获取所有ModelAttribute注解的方法,并且没有RequestMapping注解的方法
      Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
      if (!attrMethods.isEmpty()) {
         this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
      }
      //获取所有带InitBinder注解的方法
      Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
      if (!binderMethods.isEmpty()) {
         this.initBinderAdviceCache.put(adviceBean, binderMethods);
      }
      //获取实现了RequestBodyAdvice或者ResponseBodyAdvice接口的bean
      if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
         requestResponseBodyAdviceBeans.add(adviceBean);
      }
   }
//添加到requestResponseBodyAdvice集合中,优先级最高,排在最前面
   if (!requestResponseBodyAdviceBeans.isEmpty()) {
      this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
   }

}

ResponseBodyAdvice/RequestBodyAdvice的实现类有两种注册方法, 一种是直接注册到RequestMapping­HandlerAdapter, 另外一种是通过@ControllerAdvice注释, 让Spring MVC自己找到并注册, 从这里可以看到通过@ControllerAdvice注释注册的优先级更高。可以参见setRequestBodyAdvice/setResponseBodyAdvice这两个方法的调用地方,可以发现如果你是用了jackson,他就会注册两个jackson相关的处理器,用于处理MappingJacksonValue对象。

afterPropertiesSet中的三个getDefaultXXX 方法, 这三个方法非常类似,以getDefaultArgumentResolvers为例,

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

 //添加桉注解解析参数的解析器
   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());

   // 按类型解析的解析器
   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 (getCustomArgumentResolvers() != null) {
      resolvers.addAll(getCustomArgumentResolvers());
   }

   // 这两个可以解析所有类型的参数
   resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
   resolvers.add(new ServletModelAttributeMethodProcessor(true));

   return resolvers;
}

通过注释可以看到, 这里的解析器可以分为四类: 通过注解解析的解析器、 通过类型解析的解析器、 自定义的解析器和可以解析所有类型的解析器。 第三类是可以自己定义的解析器, 定义方法是自己按要求写个 resolver 然后通过 customArgumentResolvers 属性注册到 RequestMappingHandlerAdapter。需要注意的是,自定义的解析器是在前两种类型的解析器都无法解析的时候才会使用到, 这个顺序无法改变!所以如果要想自己写一个解析器来解析 @PathVariable 注释的 PathVariable 类型的参数, 是无法实现的, 即使写出来并注册到 RequestMappingHandlerAdapter 上面也不会被调用。 Spring MVC 自己定义的解析器的顺序也 是固定的,不可以改变。

处理请求的入口:handleInternal

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
   checkRequest(request);

   // Execute invokeHandlerMethod in synchronized block if required.
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }
 // 如果响应中头部 Cache-Control 头部尚未设置
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
          // 使用了 @SessionAttributes 的情况
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
      // 准备相应对象,主要是根据设置向响应对象设置跟缓存有关的头部
         prepareResponse(response);
      }
   }

   return mav;
}

这段代码中我们主要看三个方法checkRequest(request)、invokeHandlerMethod(request, response, handlerMethod)以及prepareResponse(HttpServletResponse response) ,这也对应了三个流程:

  1. 请求方式校验
  2. 真正处理请求
  3. 准备返回结果

checkRequest

先看checkRequest

//WebContentGenerator
protected final void checkRequest(HttpServletRequest request) throws ServletException {
   // 检查是否是支持的方法类型,如GET,POST
   String method = request.getMethod();
   if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
      throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
   }

   // Check whether a session is required.
   if (this.requireSession && request.getSession(false) == null) {
      throw new HttpSessionRequiredException("Pre-existing session required but none found");
   }
}

这里做了两件事:第一,检查是否是支持的方法类型,如GET,POST。

supportedMethods这是一个Set集合,默认为空,在构造函数里我们可以看到他被add的地方:

public WebContentGenerator(boolean restrictDefaultSupportedMethods) {
   if (restrictDefaultSupportedMethods) {
      this.supportedMethods = new LinkedHashSet<>(4);
      this.supportedMethods.add(METHOD_GET);
      this.supportedMethods.add(METHOD_HEAD);
      this.supportedMethods.add(METHOD_POST);
   }
   initAllowHeader();
}

如果restrictDefaultSupportedMethods为true则会被添加默认的三个方法类型,那么什么时候会为true呢,我们可以发现它的另一个无参构造函数可以将其设置为true,但是没有被使用,那么只剩下AbstractController类的初始化可以将它设置为true。在RequestMappingHandlerAdapter的父类已经将其设置为false也就是不检查请求的方法。

第二件事,如果 requireSession为 true, 则通过request.getSession(false)检查 session是否存在,如果不存在则抛出异常, 不过requireSession的默认值为false, 所以默认也不检查。

invokeHandlerMethod

接下来就是超级重要的方法->invokeHandlerMethod:

方法流程

  1. 请求处理前的参数准备,找到所有的能够设置参数或者对参数进行改变的途径,初始化成各式各样的Factory,Container;
  2. 构造ServletInvocableHandlerMethod,用于真正的请求处理;
  3. 新建传递参数的 ModelAndViewContainer 容器, 并将相应参数设置到其 Model 中;
  4. ServletInvocableHandlerMethod.invokeAndHandle处理请求;
  5. 拿到ModelAndView视图信息,做一些收尾工作。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
 // 使用request,response创建ServletWebRequest类型的webRequest
   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
   // 初始化WebDataBinderFactory,与@InitBinder注解相关
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      // 初始化ModelFactory,用于处理model
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    // 构造ServletInvocableHandlerMethod,它继承自 HandlerMethod,实际请求的处理就是通过它来执行的
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      //将所有的参数解析器放到invocableMethod里备用
      if (this.argumentResolvers != null) {
         invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }
      //将所有的结果解析器放到invocableMethod里备用
      if (this.returnValueHandlers != null) {
         invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      }
      invocableMethod.setDataBinderFactory(binderFactory);
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

      //ModelAndViewContainer承担着整个请求过程中的数据传递
      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      //针对重定向类型,提取出重定向前RedirectAttributes里设置的参数
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      //初始化model
      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();
   }
}

invokeHandleMethod 方法中首先使用 request 和 response 创建了 ServletWebRequest 类型的 webRequest, 在 ArgumentResolver 解析参数时使用的 request 就是这个webRequest, 当然如果我们的处理器需要 HttpServletRequest 类型的参数, ArgumentResolver 会给我们设置原始的 request。

接着对 WebDataBinderFactoryModelFactoryServletlnvocableHandlerMethodModelAndViewContainer 这四个类型的变量进行了定义和初始化, 下面先分别介绍一下这四个变量。

WebDataBinderFactory

WebDataBinderFactory 用来创建WebDataBinder 的, WebDataBinder用于参数绑定,主要功能就是实现参数跟String之间的类型转换, ArgumentResolver在进行参数解析的过程中会用到 WebDataBinder, 另外 ModelFactory 在更新 Model 时也会用到它。

WebDataBinderFactory 的创建过程就是将符合条件的 @InitBinder 的方法找出来, 并使用它们新建出 ServletRequestDataBinderFactory 类型的 WebDataBinderFactory。这里的 lnitBinder 方法包括两部分:

  1. 使用 @ControllerAdvice 注解的并且符合要求的全局处理器里面的 lnitBinder 方法;
  2. 处理器自身的 InitBinder 方法, 添加的顺序是先添加全局的后添加自身的。

第一类初始化过程已经塞到initBinderAdviceCache缓存中,第二类 lnitBinder 方法会在第一次调用后保存到initBinderCache缓存中, 以后直接从缓存获取就可以了。 查找注释了 @InitBinder 方法的方法和以前一样, 使用 HandlerMethodSelectorselectMethods来找, 而全局的 lnitBinder方法在创建RequestMappingHandlerAdapter的时候已经设置到缓存中了。

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//查找initBinderCache里是否存储了相关的@initBinder注解修饰的方法
   Class<?> handlerType = handlerMethod.getBeanType();
   Set<Method> methods = this.initBinderCache.get(handlerType);
   //如果没有就直接查找bean里的,并设置到缓存中
   if (methods == null) {
      methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
      this.initBinderCache.put(handlerType, methods);
   }
   List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
   // Global methods first
   //遍历`@ControllerAdvice`注解的类里找到的含有
   //@initBinder的initBinderAdviceCache,这是全局级别的,优先级最高,所以排在最前面处理
   this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
      if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
         Object bean = controllerAdviceBean.resolveBean();
         for (Method method : methodSet) {
            initBinderMethods.add(createInitBinderMethod(bean, method));
         }
      }
   });
   //将当前Handler中的InitBinder方法添加到initBinderMethods
   for (Method method : methods) {
      Object bean = handlerMethod.getBean();
      initBinderMethods.add(createInitBinderMethod(bean, method));
   }
   return createDataBinderFactory(initBinderMethods);
}

有个小细节需要注意:HandlerMethodSelector.selectMethods 的返回值是 一个Set, 如果没找到相应的方法会返回一个空Set而不是null, 所以即使没 找到InitBinder方法,initBinderCache也会为当前的Handler设置 一个空Set, 这样就可以用initBinderCache.get(handlerType)是否为null区分开没有调用过的处理器和调用过但没有 InitBinder方法的处理器。 WebDataBinderFactory主要和相应的InitBinder方法相关联。

ModelFactory

ModelFactory是用来处理Model的, 主要包含两个功能:

  1. 在处理器具体处理之前对 Model进行初始化;
  2. 在处理完请求后对Model参数进行更新。

给 Model 初始化具体包括三部分内容:

  1. 将原来的 SessionAttributes 中的值设置到 Model;
  2. 执行相应使用 @ModelAttribute 注解的方法并将其值设置到 Model;
  3. 处理器中使用 @ModelAttribute 注解的参数如果同时在 SessionAttributes 中也配置了, 而且在mavContainer 中还没有值则从全部 SessionAttributes (可能是其他处理器设置的值)中查找出并设置进去。

Model 更新是先对 SessionAttributes进行设置, 设置规则是如果处理器里调用SessionStatus#setComplete 则将 SessionAttributes 清空, 否则将 mavContainer 的 defaultModel (可以理解为 Model, 后面 ModelAndViewContainer 中会详细讲解)中相应的参数设置到 SessionAttributes 中, 然后按需要给 Model设置参数对应的 BindingResult

从这里可以看出调用 SessionStatus#setComplete 清空 SessionAttributes 是在整个处理执行完以后才执行的,也就是说这条语句在处理器中的位置并不重要, 放在处理器的开头或者结尾都不会影响当前处理器对 SessionAttributes 的使用。

我们先看ModelFactory的创建过程:

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
    // 从缓存中拿到和此Handler相关的SessionAttributesHandler处理器,如果为空则初始化
   SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
   Class<?> handlerType = handlerMethod.getBeanType();
   // 找到当前类(Controller)所有的标注的@ModelAttribute注解的方法
   Set<Method> methods = this.modelAttributeCache.get(handlerType);
   if (methods == null) {
      methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
      this.modelAttributeCache.put(handlerType, methods);
   }
   List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
   // Global methods first
   // 全局优先,放置在最前面
   this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
      if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
         Object bean = controllerAdviceBean.resolveBean();
         for (Method method : methodSet) {
            attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
         }
      }
   });
   for (Method method : methods) {
      Object bean = handlerMethod.getBean();
      attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
   }
   return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

ServletInvocableHandlerMethod

ServletInvocableHandlerMethod这个类型十分重要,一个请求的实际处理就是通过它来完成的!这里我们先只简单介绍,在下面我们详细再介绍整个层级。

它继承自HandlerMethod,并且可以直接执行, 参数绑定、处理请求以及返回值处理都在它里边完成, 创建方法 createRequestMappingMethod 代码如下:

protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
   return new ServletInvocableHandlerMethod(handlerMethod);
}
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);

这里的创建过程非常简单, 首先使用 handlerMethod 新建类 ServletlnvocableHandlerMethod 然后将 argumentResolversreturnValueHandlersbinderFactoryparameterNameDiscoverer设置进去就完成了。

这三个变量创建完之后的工作还有三步(这里省略了异步处理):

  1. 新建传递参数的 ModelAndViewContainer 容器, 并将相应参数设置到其 Model 中;
  2. 执行请求;
  3. 请求处理完后进行一些后置处理。

新建 ModelAndViewContainer 类型的 mavContainer 参数,用于保存 Model 和 View,它贯穿于整个处理过程(注意, 在处理请求时使用的并不是 ModelAndView), 然后 对 mavContainer 进行了设置, 主要包括三部分内容:

  1. 将 FlashMap 中的数据设置到 Model ;
  2. 使用 modelFactory 将 SessionAttributes 和注释了 @ModelAttribute 的方法的参数设置到 Model;
  3. 根据配置对 ignoreDefaultModelOnRedirect 进行了设置, 这个参数在分析 ModelAndViewContainer 的时候再详细讲解。

设置代码如下:

ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

到这里传递参数的容器就准备好了。 设置完mavContainer 后又做了一些异步处理的相关的工作, 异步处理我们可以暂时先跳过。

执行请求, 具体方法是直接调用ServletlnvocableHandlerMethod里的invokeAndHandle
方法执行的, 代码如下:

invocableMethod.invokeAndHandle(webRequest, mavContainer);

ServletlnvocableHandlerMethodinvokeAndHandle方法执行请求的具体过程在后面分析 ServletlnvocableHandlerMethod的时候再详细讲解。

处理完请求后的后置处理 ,这是在 getModelAndView方法中处理的。 一共做了三件事:

  1. 调用ModelFactoryupdateModel方 法更新了Model (包括设置了 SessionAttributes 和给 Model设 置BindingResult) ;
  2. 根据 mavContainer创建了 ModelAndView ;
  3. 如果mavContainer里的model是RedirectAttributes类型则其值设置到FlashMap中。
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
      ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

   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) {
         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
      }
   }
   return mav;
}

这里的model只有处理器在返回redirect类型的视图时才可能是RedirectAttributes 类型 ,否则不会是RedirectAttributes类型, 也就是说在不返回redirect类型视图的处理器中即使使用RedirectAttributes设置了变量也不会保存到FlashMap中,具体细节介绍ModelAndViewContainer的时候再详细分析。

ModelAndViewContainer

ModelAndViewContainer承担着整个请求过程中的数据传递,除了字面上保存model以及view还有一些其他的功能,先看看这个容器内包含的属性

private boolean ignoreDefaultModelOnRedirect = false;

//视图,Object类型,可以是实际视图也可以是逻辑视图
@Nullable
private Object view;
//默认使用的model
private final ModelMap defaultModel = new BindingAwareModelMap();
//redirect类型的model
@Nullable
private ModelMap redirectModel;
//处理器返回redirect视图的标志(提供set方法设置进来)
private boolean redirectModelScenario = false;
// Http状态码
@Nullable
private HttpStatus status;
// 记录通过注解 @ModelAttribute(binding=true/false) 的参数设置为不要绑定的属性的名称
private final Set<String> noBinding = new HashSet<>(4);
 // 用于记录初始化时注解@ModelAttribute(binding=true/false)设置的禁止绑定的属性的集合
private final Set<String> bindingDisabled = new HashSet<>(4);
// 和@SessionAttributes标记的元素有关
private final SessionStatus sessionStatus = new SimpleSessionStatus();
//标记请求是否已经被handler处理完成的标志,这个标记很重要
private boolean requestHandled = false;

defaultModelredirectModel, 这两个都是Model, 前者是默认使用的Model, 后者用于传递redirect 时的参数。 我们在处理器中使用了Model或者ModelMap 时ArgumentResolver会传入defaultModel, 它是BindingAwareModelMap类型, 既继承 了ModelMap又实现了Model接口, 所以在处理器中使用Model或者ModelMap其实使用的是同个对象, Map参数传入的也是这个对象。 处理器中 RedirectAttributes类型的参数ArgumentResolver会传入redirectModel, 它实际上是RedirectAttributesModelMap类型。ModelAndViewContainergetModel方法会根据条件返回其中的一个:

public ModelMap getModel() {
   if (useDefaultModel()) {
      return this.defaultModel;
   }
   else {
      if (this.redirectModel == null) {
         this.redirectModel = new ModelMap();
      }
      return this.redirectModel;
   }
}

判断逻辑在useDefaultModel中,如果 redirectModelScenario为false, 也就是返回的不是redirect视图的时候一定返回defaultModel, 如果返回redirect视图的情况下需要根据 redirectModelignoreDefaultModelOnRedirect 的情况进一步,如果 redirectModel 不为空和 ignoreDefaultModelOnRedirect设置为true 这两个条件中有一个成立则返回redirectModel, 否则返回defaultModel。 总结如下:
返回defaultModel的情况:

  1. 处理器返回的不是redirect视图;
  2. 处理器返回的是redirect 视图但是redirectModel为null, 而且ignoreDefatiltModelOnRedirect 也是false。

返回redirectModel的情况:

  1. 处理器返回redirect视图, 并且redirectModel 不为null;
  2. 处理器返回的是redirect视图,并且ignoreDefaultModelOnRedirect为true。

ignoreDefaultModelOnRedirect可以在RequestMappingHandlerAdapter中设置,这个参数默认是false,除非你显示注册了一个RequestMappingHandlerAdapter的bean并将其设置为你想要的布尔值 。 判断处理器返回的是不是redirect视图的标志设置在redirectModelScenario中,它是在ReturnValueHandler 中设置的,ReturnValueHandler如果判断到是redirect视图就会将redirectModelScenario设置为 true。 也就是说在ReturnValueHandler 处理前ModelAndViewContainergetModel返回的一定是 defaultModel, 处理后才可能是 redirectModel 。

那么也许你可能有疑问是不是随便像下面这样就能产生redirectModel :

@GetMapping("/redirectJsp")
public String redirectJsp() {
   return "redirect:/showJsp";
}

当然不是这样,找到setRedirectModel的地方,我们发现在参数处理器中发现了RedirectAttributesMethodArgumentResolver,通过这个类头上的注释我们可以了解到,原来这个类能够生效需要参数带RedirectAttributes类型,也就是像如下:

@GetMapping("/redirect-handler")
public String testredirect(Model model, RedirectAttributes attr) {
   attr.addFlashAttribute("test", "小陈真好");
   attr.addFlashAttribute("u2", "GOOD GOOD STUDY");
   return "redirect:/showJsp";
}

现在再返回去看 RequestMappingHandlerAdapter 中的 getModelAndView方法 getModel 后判断 Model 是不是 RedirectAttributes 类型就知道原来是为了跳转后请求做参数准备。 在 getModel 返回redirectModel 的情况下, 在处理器中设置到 Model 中的参数就不会被使用了(设置 SessionAttribute 除外)。这样也没有什么危害, 因为只有 redirect 的情况才会返回 redirectModel , 而这种情况是不需要渲染页面的, 所以 defaultModel 里的参数本来也没什么用。 同样,如果返回defaultModel, 设置到 RedirectAttributes 中的参数也将丢弃, 也就是说在返回的View不是 redirect 类型时,即使处理器使用 RedirectAttributes 参数设置了值也不会传递到下一个请求。

另外, 通过 @SessionAttribute 传递的参数是在ModelFactory 中的 updateModel 方法中设 置的, 那里使用了 mavContainer.getDefaultModel 方法, 这样就确保无论在什么情况下都是使用defaultModel, 也就是只有将参数设置到 Model 或者 ModelMap 里才能使用 SessionAttribute 缓存, 设置到 RedirectAttributes 里的参数不可以。
ModelAndViewContainer 还提供了添加、 合并和删除属性的方法,他们都是直接调用model操作的。

bindingDisablednoBinding我们在上面的注释有提到是为了@ModelAttribute(binding=true/false)这个注解服务的,前者是查找方法级别,后者是函数参数级别,在ModelAndViewContainer中提供了一个方法用于在参数处理阶段过滤那部分不需要绑定的参数

public boolean isBindingDisabled(String name) {
   return (this.bindingDisabled.contains(name) || this.noBinding.contains(name));
}

sessionStatus 属性就是在处理器中通知 SessionAttribute 已经使用完时所用到的SessionStatus类型的参数, 它用于标示SessionAttribute是否已经使用完,如果使用完了 则在ModelFactoryupdateModel方法中将SessionAttribute的相应参数清除, 否则将当前 Model的相应参数设置进去。
requestHandled用于标示请求是否已经全部处理完, 如果是就不再往下处理,直接返回。这里的全部处理完主要指已经返回response, 比如, 在处理器返回值有@ResponseBody注解或者返回值为HttpEntity类型等情况都会将requestHandled设置为true。

modelFactory#initModel

在初步了解了invokeHandlerMethod方法中最重要的四个参数之后,我们再接下去看modelFactory.initModel(webRequest, mavContainer, invocableMethod);方法

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
      throws Exception {
//获取所有的@SessionAttribute注解保存的参数,并合并到container中
   Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
   container.mergeAttributes(sessionAttributes);
   // 执行使用@ModelAttribute注解的方法,并将结果设置到mavContainer
   invokeModelAttributeMethods(request, container);
//请求访问完之后将修改的值重新放到@SessionAttributeStore设置的key中
   for (String name : findSessionAttributeArguments(handlerMethod)) {
      if (!container.containsAttribute(name)) {
         Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
         if (value == null) {
            throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
         }
         container.addAttribute(name, value);
      }
   }
}
流程
  1. SessionAttributes 中取出保存的参数, 并合并到 mavContainer 中;
  2. 执行使用 @ModelAttribute 注解的方法并将结果设置到Model;
  3. 判断既使用了@ModelAttribute 注解又使用 @SessionAttributes 注解中(参数名或者参数类型在注解中设置着)的参数是否已经设置到了 mavContainer 中, 如果没有则使用 SessionAttributesHandlerSessionAttributes 中获取并设置到mavContainer 中。

首先SessionAttributesHandler在上面的ModelFactory的介绍有看到过一次了。是用来处理@SessionAttributes注解的参数,不过它只是做一些宏观的事情,比如, 哪个Handler都可以缓存哪些参数、 某个参数在当前的SessionAttributes中是否存在、如何同时操作多个参数等,而具体的存储工作是交给SessionAttributeStore去做的,不过SessionAttributeStore并不是保存数据的容器,而是用于保存数据的工具, 具体保存数据的容器默认使用的是Session, 当然也可以使用别的容器,重写SessionAttributeStore然后设置到RequestMappingHandlerAdapter, 在SessionAttributeStore中保存到别的容器就可以了,如果集群就可以考虑一下。

SessionAttributesHandler里面有四个属性:

属性名类型作用
attributeNamesSet存储@SessionAttributes 注释里value对应的值,即参数名
attributeTypesSet存储@SessionAttributes注释里 types 对应的值,即参数类型
knownAttributeNamesSet存储所有已知可以被当前处理器处理的属性名
sessionAttributeStoreSessionAttributeStore用于具体执行Attribute的存储工作

SessionAttributesHandlerRequestMappingHandlerAdapter#getModelFactory中进行创建,关注SessionAttributesHandler方法,通过查找当前请求handler所在的类上有@SessionAttributes注解的信息:

private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
   return this.sessionAttributesHandlerCache.computeIfAbsent(
         handlerMethod.getBeanType(),
         type -> new SessionAttributesHandler(type, this.sessionAttributeStore));
}
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
   Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
   this.sessionAttributeStore = sessionAttributeStore;

   SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
   if (ann != null) {
      Collections.addAll(this.attributeNames, ann.names());
      Collections.addAll(this.attributeTypes, ann.types());
   }
   this.knownAttributeNames.addAll(this.attributeNames);
}

knownAttributeNames的值来自两个地方,一个就是上面构造函数里add进去的,其次当调用isHandlerSessionAttribute方法检查,而且是当前Handler所管理的SessionAttributes的时候也会添加到knownAttributeNames,而保存属性的storeAttributes方法会在每个属性保存前调用 isHandlerSessionAttribute方法判断是否支持要保存的属性, 所以所有保存过的属性的名称都会被保存在 knownAttributeNames 里面。 knownAttributeNames 作用主要是保存了除了使用 value 配置的名称外还将通过 types 配置的已经保存过的属性名保存起来,这样在清空的时候只需要遍历 knownAttributeNames 就可以了。

public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
   Assert.notNull(attributeName, "Attribute name must not be null");
   if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
   //这里
      this.knownAttributeNames.add(attributeName);
      return true;
   }
   else {
      return false;
   }
}

SessionAttributesHandler 中除了保存属性 storeAttributes 的方法外还有取回属性值的 retrieveAttributes方法和清空属性的 cleanupAttributes方法, 它们都是根据 knownAttributeNames 来操作的。 另外还有按属性名取回属性的 retrieveAttribute方法,具体可以直接查看源码文件。

需要注意的是, 在取出全部属性和清除属性时都遍历了 knownAttributeNames, 前面说过
它里面保存着当前 Handler 注释里所有使用过的属性名, 所以这两个方法的操作只是对当前
处理器类的 @SessionAttributes 注释里配置的属性起作用, 而按名称取属性的方法可以在整个 SessionAttributes 中查找, 没有 knownAttributeNames 的限制。 另外需要注意的是 如果在不
同的 Handler 中用 SessionAttributes 保存的属性使用了相同的名称, 它们会相互影响。

public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
   attributes.forEach((name, value) -> {
      if (value != null && isHandlerSessionAttribute(name, value.getClass())) {
         this.sessionAttributeStore.storeAttribute(request, name, value);
      }
   });
}

public Map<String, Object> retrieveAttributes(WebRequest request) {
   Map<String, Object> attributes = new HashMap<>();
   for (String name : this.knownAttributeNames) {
      Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
      if (value != null) {
         attributes.put(name, value);
      }
   }
   return attributes;
}

public void cleanupAttributes(WebRequest request) {
   for (String attributeName : this.knownAttributeNames) {
      this.sessionAttributeStore.cleanupAttribute(request, attributeName);
   }
}

@Nullable
Object retrieveAttribute(WebRequest request, String attributeName) {
   return this.sessionAttributeStore.retrieveAttribute(request, attributeName);
}

具体对每个参数保存、 取回和删除的工作是由 SessionAttributeStore 完成的。 这是一个接口, 里面的三个方法分别对应上述三个功能, 它的默认实现类是 DefaultSessionAttributeStore, 它将参数保存到 Session 中,它的具体调用我们在下面再讲,回到initModel方法,看invokeModelAttributeMethods

private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
      throws Exception {

   while (!this.modelMethods.isEmpty()) {
   //拿到方法
      InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
      //找到带ModelAttribute注解里的value作为参数名
      //跳过不需要绑定的
      ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
      Assert.state(ann != null, "No ModelAttribute annotation");
      if (container.containsAttribute(ann.name())) {
         if (!ann.binding()) {
            container.setBindingDisabled(ann.name());
         }
         continue;
      }
//执行
      Object returnValue = modelMethod.invokeForRequest(request, container);
      if (!modelMethod.isVoid()){
      //获取参数名
         String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
         if (!ann.binding()) {
            container.setBindingDisabled(returnValueName);
         }
         //赋值
         if (!container.containsAttribute(returnValueName)) {
            container.addAttribute(returnValueName, returnValue);
         }
      }
   }
}

这里遍历每个使用 @ModelAttribute 注解的方法, 然后从注解中获取参数名, 如果获取到了(注释中设置了 value), 而且在 mavContainer 中已经存在此参数了则跳过此方法, 否则执行方法(这时的方法封装成了 lnvocableHandlerMethod 类型, 可以直接执行, 后面会详细讲解), 执行完之后判断返回值是不是 Void 类型, 如果是则说明这个方法是自已将参数设置到 Model 中的, 这里就不处理了, 否则使用 getNameForReturnValue 方法获取到参数名, 并判断是否已存在 mavContainer 中, 如果不存在则设置进去。

public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) {
   ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
   if (ann != null && StringUtils.hasText(ann.value())) {
      return ann.value();
   }
   else {
      Method method = returnType.getMethod();
      Assert.state(method != null, "No handler method");
      Class<?> containingClass = returnType.getContainingClass();
      Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
      return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
   }
}

首先获取了返回值的 @ModelAttribute 注释, 也就是方法的 @ModelAttribute 注释, 如果设置了 value 则直接将其作为参数名返回, 否则使用 Conventions的静态方法 getVariableNameForReturnType,根据方法、 返回值类型和返回值获取参数名,这里不做展开。

第三步找到既使用了@ModelAttribute 注解又使用 @SessionAttributes 注解的参数,看是不是已经被放置进mavContainer ,如果没有则放置。

如下是找到@ModelAttribute 注解又使用 @SessionAttributes 注解的参数:

private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
   List<String> result = new ArrayList<>();
   for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
      if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
         String name = getNameForParameter(parameter);
         Class<?> paramType = parameter.getParameterType();
         if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
            result.add(name);
         }
      }
   }
   return result;
}

遍历方法里的每个参数, 如果有 @ModeIAttribute 注解则获取到它对应的参数名,然后用获取到的参数名和参数的类型检查是不是在 @SessionAttributes 注解中, 如果在则符合要求。

invocableMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
// 执行请求
   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   //处理@ResponseStatus注解
   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;
   }
}
流程
  1. 执行请求
  2. 判断是否已经是处理完成
  3. 对返回结果进行处理

在真正进入这个方法源码分析之前我们还需要了解这个类的相关信息,在上面我们提到了ServletInvocableHandlerMethod,它的本质是一个HandlerMethod。现在我们来详细地认识一下这个ServletInvocableHandlerMethod整个层级源码。

ServletInvocableHandlerMethod
ServletInvocableHandlerMethod

如上图,我们知道Spring划分了三层,那么这么划分的意义是什么?每一层都做了什么?

  1. HandlerMethod:封装Handler以及处理具体请求的method;
  2. lnvocableHandlerMethod:通过HandlerMethodArgumentResolver处理参数执行处理具体请求的method(严格来说应该是bridgedMethod);
  3. ServletInvocableHandlerMethod:使用HandlerMethodReturnValueHandler处理返回值,对基于方法级别的@ResponseStatus注解的支持。

先看HandlerMethod

//当前handler的bean,可能传入的是一个String类型的bean
private final Object bean;

//bean工厂,用于当bean属性是Sring的beanName,可以用beanName获取到对应的bean作用Handler
private final BeanFactory beanFactory;
// 方法所属类
private final Class<?> beanType;
// 注册的方法
private final Method method;
// 被桥接的方法,如果method是原生的,这个属性的值就是method
private final Method bridgedMethod;
 // 封装方法参数的类实例,一个MethodParameter就是一个参数
private final MethodParameter[] parameters;
// Http状态码
@Nullable
private HttpStatus responseStatus;
// ResponseStatus注解的reason值
@Nullable
private String responseStatusReason;
// 通过createWithResolvedBean()解析此handlerMethod实例的handlerMethod。
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
 // 标注在接口入参上的注解们(此处数据结构复杂,List+二维数组)
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
//toString使用
private final String description;

bridgedMethod表示被桥接的方法,由于Java泛型会存在类型擦除,所以在Java中的多态中泛型可能会带来两个问题: 1. 类型擦除与多态的冲突;2. 方法签名冲突,如下示例

public interface Parent<T> {
    T bridgeMethod(T param);
}

// 定义一个类实现泛型接口
public class Child implements Parent<String> {
    public String bridgeMethod(String param) {
        return param;
    }
}
public class BridgeMethodTest {
    public static void main(String[] args) throws Exception {
       // 使用java的多态
        Parent parent = new Child();
        System.out.println(parent.bridgeMethod("abc123"));
    }
}

所以为了解决这个问题,就需要编译器在子类中生成一个桥方法(bridge method), 用到了强制转换类型:

public void bridgeMethod(Object param) { setSecond((String) param); }

虚拟机调用桥方法,桥方法再调用子类的真正复写的方法。

如果不涉及泛型,bridgedMethod和method是同一个方法,bridgedMethod是为了找到最原始的方法,即不带泛型的方法。在spring中获取原方法bridgedMethod是通过BridgeMethodResolver. findBridgedMethod来完成的, 大概思路是按相同的方法名和相同的参数个数找不是桥的方法。

再看parameters,它的类型是MethodParameter,从类型名称上可以理解他是方法参数的数组,

parameterName//参数所在的方法,Method的父类,Java8新加的
private final Executable executable;
//参数序号,从0开始,表示第几个参数
private final int parameterIndex;
@Nullable
private volatile Parameter parameter;
//嵌套层级,复合参数使用,例如List<String> params,params为1,内部的String嵌套层级是2
private int nestingLevel;

//每层嵌套参数的序数
@Nullable
Map<Integer, Integer> typeIndexesPerLevel;

//容器类型,参数所属方法所在的类
@Nullable
private volatile Class<?> containingClass;
//参数类型
@Nullable
private volatile Class<?> parameterType;
//Type型的参数类型,也就是参数类型,但他的类型是Type
@Nullable
private volatile Type genericParameterType;
//参数的注解
@Nullable
private volatile Annotation[] parameterAnnotations;
//参数名称查找器
@Nullable
private volatile ParameterNameDiscoverer parameterNameDiscoverer;
//参数名称
@Nullable
private volatile String parameterName;
//嵌套的方法参数
@Nullable
private volatile MethodParameter nestedMethodParameter;

MethodParameter里最重要的是 executableparameterlndex, 有了这两个参数后参数类型、注解等都可以获取到。 不过在正常的反射技术里是不知道参数名(parameterName)的, 所以这里专门使用了 一个参数名查找的组件parameterNameDiscoverer, 它可以查找出我们定义参数时参数的名称, 这就给ArgumentResolver的解析提供了方便。

HandlerMethod中定义了两个内部类来封装参数,一个封装方法调用的参数, 一个封装方法返回的参数, 它们主要使用 methodparameterlndex来创建MethodParameter, 封装返回值的 ReturnValueMethodParameter继承自封装调用参数的HandlerMethodParameter, 它们使用的 method都是bridgedMethod, 返回值使用的parameterIndex是-1,

/**
 * A MethodParameter with HandlerMethod-specific behavior.
 */
protected class HandlerMethodParameter extends SynthesizingMethodParameter {

   @Nullable
   private volatile Annotation[] combinedAnnotations;

   public HandlerMethodParameter(int index) {
      super(HandlerMethod.this.bridgedMethod, index);
   }

   protected HandlerMethodParameter(HandlerMethodParameter original) {
      super(original);
   }
   ....省略....
   }
  
/**
 * A MethodParameter for a HandlerMethod return type based on an actual return value.
 */
private class ReturnValueMethodParameter extends HandlerMethodParameter {

   @Nullable
   private final Object returnValue;

   public ReturnValueMethodParameter(@Nullable Object returnValue) {
      super(-1);
      this.returnValue = returnValue;
   }

   protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
      super(original);
      this.returnValue = original.returnValue;
   }

   @Override
   public Class<?> getParameterType() {
      return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
   }

   @Override
   public ReturnValueMethodParameter clone() {
      return new ReturnValueMethodParameter(this);
   }
}

lnvocableHandlerMethod继承自HandlerMethod, 在父类基础上添加了调用的功能, 也
就是说InvocableHandlerMethod可以直接调用内部属性method对应的方法(严格来说应该是
bridgedMethod) 。
lnvocableHandlerMethod里增加了三个属性:

  1. dataBinderFactory : WebDataBinderFactory类型, 可以创建WebDataBinder, 用于参数
    解析器ArgumentResolver中。
  2. argumentResolvers: HandlerMethodArgumentResolverComposite类型, 用于解析参数。
  3. parameterNameDiscoverer : ParameterNameDiscoverer类型, 用来获取参数名, 用于
    MethodParameter中。

现在我们回到invocableMethod#invokeAndHandle方法中去,先看调用器父类的InvocableHandlerMethod#invokeForRequest

@Nullable
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);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
//拿到参数数组
   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }

   Object[] args = new Object[parameters.length];
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      //设置参数名解析器
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      //如果相应类型的参数已经在providedArgs中提供了,则直接设置到parameter
      args[i] = findProvidedArgument(parameter, providedArgs);
      if (args[i] != null) {
         continue;
      }
      if (!this.resolvers.supportsParameter(parameter)) {
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
      //使用ArgumentResolver解析参数
         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;
}

首先调用父类的 getMethodParameters 方法获取到 Method 的所有参数, 然后定义了 Object 数组变量 args 用于保存解析出的参数值。 接下来遍历每个参数进行解析, 解析的方法有两种, 第一种是在 providedArgs 里面找, 第二种是使用 argumentResolvers 解析, 在 RequestMappingHandlerAdapter 中的调用并没有提供 providedArgs, 所以只有使用 argumentResolvers 解析。 argumentResolvers 有很多,我们将参数解析器以及结果处理器留到下一篇具体再分析。

处理完参数就直接到doInvoke方法,通过反射完成我们的请求处理。

完成invokeForRequest方法的执行之后,继续往下看,开始处理 @ResponseStatus 注解, 最后处理返回值。 处理 @ResponseStatus 注释的方法是 setResponseStatus, 它会根据注解的值设置 response 的相关属性。最后执行returnValueHandlers#handleReturnValue对结果进行处理。同样结果的处理我们留到下篇再讲因为涉及大量的处理类。

getModelAndView

终于在我们处理完请求之后我们要开始将结果组装成model以及视图

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
      ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
   //处理sessionAttributes的存储以及清空,更新@InitBinder注解结果的绑定
   modelFactory.updateModel(webRequest, mavContainer);
   //判断,如果当前请求已经处理完成,则不进行后续的处理没直接返回null
   if (mavContainer.isRequestHandled()) {
      return null;
   }
    //如果请求还未处理完成,那说明可能有页面需要返回,开始查找,处理并返回mav
   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) {
         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
      }
   }
   return mav;
}

小结

至此我们终于将HandlerAdapter结构都分析了一遍,其中最重要的当属handleInternal方法,涉及到请求的真正处理过程。一个请求进来的大致经过:

  1. 请求方式校验
  2. 处理请求:(1)准备参数(2)执行方法(3)处理返回结果
  3. 准备返回结果

因为每个handler的入参,返回结果各异,所以Spring使用了反射来执行真正的方法。我们在本文中知道了一个请求的处理流程我们其实还是有一些疑问,参数绑定是怎么真正被绑定上去的?我们入参是json的body,或者params是怎么变成一个个对象到controller,返回结果又是怎么从对象变成JSON到前端去的呢?我们下篇文章来仔细讲讲参数解析器以及结果处理器。