天天看點

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

  這一章我們主要是梳理下HandlerAdapter,在看這一篇的時候可以去看下前面的關于HandlerMapping與處理器映射器前置知識的内容,怎樣能更好的了解。

   1、HandlerAdapter接口:

public interface HandlerAdapter {

   boolean supports(Object handler);

   @Nullable
   ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

   long getLastModified(HttpServletRequest request, Object handler);
}
           

    可以看到這個接口有三個方法。

   1、supports方法

         這個接口就是看這類是用來處理哪種handler的。

   2、handle方法

   這個方法就是具體的處理方法,通過前面的文章的梳理,可以看到Spring在進行選擇的時候一般是通過supports方法判斷選擇哪個,然後再用handle方法去具體處理。

   3、getLastModified

       這個就是看這個handler最後的修改時間,,有些參數能在沒有修改的時候不進行處理。

  下面我們來看下其的實作類

  2、SimpleControllerHandlerAdapter

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

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

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

      return ((Controller) handler).handleRequest(request, response);
   }

   @Override
   public long getLastModified(HttpServletRequest request, Object handler) {
      if (handler instanceof LastModified) {
         return ((LastModified) handler).getLastModified(request);
      }
      return -1L;
   }
}
           

  這裡可以看到,這個HandlerAdapter就是通過supports看下目前要處理的handler是不是Controller。如果是,就通過handle去處理。

 3、SimpleServletHandlerAdapter

public class SimpleServletHandlerAdapter implements HandlerAdapter {

   @Override
   public boolean supports(Object handler) {
      return (handler instanceof Servlet);
   }
   @Override
   @Nullable
   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      ((Servlet) handler).service(request, response);
      return null;
   }
   @Override
   public long getLastModified(HttpServletRequest request, Object handler) {
      return -1;
   }
}
           

 這個HandlerAdapter supports方法就是判斷handler是不是Servlet,如果是就用handle去處理,直接調用Servlet的service方法。傳回的為null。

 4、HttpRequestHandlerAdapter

public class HttpRequestHandlerAdapter implements HandlerAdapter {

   @Override
   public boolean supports(Object handler) {
      return (handler instanceof HttpRequestHandler);
   }
   @Override
   @Nullable
   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {

      ((HttpRequestHandler) handler).handleRequest(request, response);
      return null;
   }
   @Override
   public long getLastModified(HttpServletRequest request, Object handler) {
      if (handler instanceof LastModified) {
         return ((LastModified) handler).getLastModified(request);
      }
      return -1L;
   }
}
           

  可以看到這個Adapter是用來處理HttpRequestHandler的,handle調用的是其的handleRequest方法。這個HttpRequestHandler我的了解其實這個與Servlet是類似的(因為其入參一樣是HttpServletRequest 、HttpServletResponse ),你可以自定義處理器去實作HttpRequestHandler這個接口。

     SpringMVC對HttpRequestHandler的實作一般會用到的應該是兩個

  1、DefaultServletHttpRequestHandler

public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
       ............
   @Override
   public void setServletContext(ServletContext servletContext) {
      this.servletContext = servletContext;
      if (!StringUtils.hasText(this.defaultServletName)) {
         if (this.servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) {
            this.defaultServletName = COMMON_DEFAULT_SERVLET_NAME;
         }
         else if (this.servletContext.getNamedDispatcher(GAE_DEFAULT_SERVLET_NAME) != null) {
            this.defaultServletName = GAE_DEFAULT_SERVLET_NAME;
         }
         else if (this.servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) {
            this.defaultServletName = RESIN_DEFAULT_SERVLET_NAME;
         }
         else if (this.servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null) {
            this.defaultServletName = WEBLOGIC_DEFAULT_SERVLET_NAME;
         }
         else if (this.servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null) {
            this.defaultServletName = WEBSPHERE_DEFAULT_SERVLET_NAME;
         }
         else {
            throw new IllegalStateException("Unable to locate the default servlet for serving static content. " +
                  "Please set the 'defaultServletName' property explicitly.");
         }
      }
   }
   @Override
   public void handleRequest(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {

      Assert.state(this.servletContext != null, "No ServletContext set");
      RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
      if (rd == null) {
         throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
               this.defaultServletName + "'");
      }
      rd.forward(request, response);
   }
}
           

   這裡有些tomcat的内容,這裡先挖一個坑。

可以看到其是先通過setServletContext方法,看是哪種getNamedDispatcher,再将defaultServletName進行對應的設定,然後在通過handleRequest方法,擷取到RequestDispatcher(tomcat)的類,再調用其的forward方法。

  2、ResourceHttpRequestHandler

   這個類看名稱就是知道其是用來處理Resource的,這裡簡答看下其的handleRequest方法:

@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   Resource resource = getResource(request);
      ............
   MediaType mediaType = getMediaType(request, resource);
    ........
   ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
   if (request.getHeader(HttpHeaders.RANGE) == null) {
      setHeaders(response, resource, mediaType);
      this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
   }
   else {
      response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
      ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
      try {
         ...........
         this.resourceRegionHttpMessageConverter.write(
               HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage);
      }
      catch (IllegalArgumentException ex) {
         response.setHeader("Content-Range", "bytes */" + resource.contentLength());
         response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
      }
   }
}
           

 可以看到,這裡分為5個部分:

     1、通過request,擷取對應的Resource。

     2、擷取對應的MediaType。

     3、通過response建構outputMessage。

     4、設定response的頭部。

     5、通過this.resourceRegionHttpMessageConverter.write将resource的内容寫到response中。

  1、我們再通過一個demo來更直接的了解:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <bean id="beanNameUrlHandlerMapping"
          class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    </bean>
    <bean id="resourceHandler1" name="/ResourceHandlerHello.html"
          class="org.springframework.web.servlet.resource.ResourceHttpRequestHandler">
        <property name="locations">
           <list>
               <ref bean="myClassPathResource"/>
           </list>
        </property>
    </bean>

    <bean id="myClassPathResource" class="org.springframework.core.io.ClassPathResource">
        <constructor-arg name="path" value="static/"/>
    </bean>
</beans>
           

  然後在maven項目的resource目錄建立static目錄,建立ResourceHandlerHello.html靜态界面

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        Hello ResourceHttpRequestHandler
</body>
</html>
           
SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

 啟動tomcat項目,通路http://localhost:8080/ResourceHandlerHello.html 位址就能傳回界面:

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

         下面 我們來debug看下流程,前面擷取到handler(resourceHandler1)我們就不再梳理了,可以看以前關于HandlerMapping的内容。這裡會根據這個bean标簽的name="/ResourceHandlerHello.html"将其注冊到BeanNameUrlHandlerMapping中,然後在請求的時候就會将對應的ResourceHttpRequestHandler傳回,然後會根據這個ResourceHttpRequestHandler(HttpRequestHandler)擷取到對應的HttpRequestHandlerAdapter,我們現在來看下這個handler的handleRequest方法:

    1、先是getResource

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

然後這裡的getLocations方法就是我們添加的 <ref bean="myClassPathResource"/>:

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結
SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

可以看到這裡之後就擷取到這個resource了,擷取resource後,将将其寫到response中,這個可以看前面關于ResourceHttpRequestHandler的handleRequest方法五部介紹。

5、RequestMappingHandlerAdapter

  現在就來到了算是所有HandlerAdapter中很複雜的的RequestMappingHandlerAdapter了(這也是在梳理HandlerAdapter之前,梳理了一些字首内容)  

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
      implements BeanFactoryAware, InitializingBean {      

   這章我們直接來開下其的一些主要内容,是以就不具體的介紹其的父類、及其他的一些内容了(有一些主要方法來展開這些點)

  1、方法

      1、構造函數(RequestMappingHandlerAdapter)

public RequestMappingHandlerAdapter() {
   StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
   stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

   this.messageConverters = new ArrayList<>(4);
   this.messageConverters.add(new ByteArrayHttpMessageConverter());
   this.messageConverters.add(stringHttpMessageConverter);
   this.messageConverters.add(new SourceHttpMessageConverter<>());
   this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
private List<HttpMessageConverter<?>> messageConverters;
      

    可以看到RequestMappingHandlerAdapter的構造函數就是設定HttpMessageConverter,這個HttpMessageConverter前面梳理HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler接口的時候有了解,一般是用與實作AbstractMessageConverterMethodArgumentResolver抽象類中,用來操作request body/response body的。

    2、getDefaultArgumentResolvers(RequestMappingHandlerAdapter)

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
   List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
     ..........
   resolvers.add(new ServletModelAttributeMethodProcessor(false));
   resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
   resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    ..........
   resolvers.add(new ModelMethodProcessor());
   .............
   return resolvers;
}      

    可以看到這裡在建構RequestResponseBodyMethodProcessor(HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler實作類)的時候,會傳入前面設定的預設HttpMessageConverter。

   3、afterPropertiesSet(RequestMappingHandlerAdapter)

     這個是因為其實作了InitializingBean接口

@Override
public void afterPropertiesSet() {
   // Do this first, it may add ResponseBody advice beans
   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);
   }
}      

     可以看到這裡是有建立argumentResolvers(HandlerMethodArgumentResolverComposite)、returnValueHandlers(HandlerMethodReturnValueHandlerComposite),将前面的那些resolvers(HandlerMethodArgumentResolver)、handlers(HandlerMethodReturnValueHandler)傳入完成其的初始化。

  4、initControllerAdviceCache

   這個方法是上面afterPropertiesSet調用的  

private void initControllerAdviceCache() {
       ..............
   List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
   AnnotationAwareOrderComparator.sort(adviceBeans);
   List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
   for (ControllerAdviceBean adviceBean : adviceBeans) {
      Class<?> beanType = adviceBean.getBeanType();
       .......
      Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
      if (!attrMethods.isEmpty()) {
         this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
      }
      Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
          .......... // this.initBinderAdviceCache.put(adviceBean, binderMethods);
      if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
         requestResponseBodyAdviceBeans.add(adviceBean);
      }
      if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
         requestResponseBodyAdviceBeans.add(adviceBean);
      }
   }
     .........
}
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
   List<ControllerAdviceBean> beans = new ArrayList<>();
   for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
      if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
         beans.add(new ControllerAdviceBean(name, applicationContext));
      }
   }
   return beans;
}      

   這裡就是從BeanFactory中擷取所有bean(Object),然後判斷該bean有沒有注解ControllerAdvice,如果是就産生ControllerAdviceBean添加到beans中,然後再周遊。

   這裡有兩個MethodFilter

public static final MethodFilter INIT_BINDER_METHODS = method ->
      AnnotationUtils.findAnnotation(method, InitBinder.class) != null;

public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
      ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
      (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));      

 如果bean的方法有InitBinder、RequestMapping這些注解,就将其添加到Cache中,下面同樣是判斷RequestBodyAdvice、ResponseBodyAdvice。

  5、handleInternal

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   ModelAndView mav;
   checkRequest(request);
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      ........這裡是與response中有些cache設定有關
   }
   return mav;
}
private boolean synchronizeOnSession = false;      

   這裡就是整個RequestMappingHandlerAdapter的的主要方法handleInternal

   首先是checkRequest,可以看到這裡是對method 、Session的一些判斷。requireSession 預設為false,如果主動修改為true,就會校驗

protected final void checkRequest(HttpServletRequest request) throws ServletException {
   // Check whether we should support the request method.
   String method = request.getMethod();
   if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
      throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
   }

   if (this.requireSession && request.getSession(false) == null) {
      throw new HttpSessionRequiredException("Pre-existing session required but none found");
   }
}
private boolean requireSession = false;      

   然後是看需不需要使用同步,調用invokeHandlerMethod方法。

  6、invokeHandlerMethod

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
      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 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);
        .........
      if (asyncManager.hasConcurrentResult()) {
         ..........
      }

      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}
public boolean hasConcurrentResult() {
   return (this.concurrentResult != RESULT_NONE);
}
private Object concurrentResult = RESULT_NONE;      

  這篇文章之前的那些前置知識點,主要就是用在這裡了。

   7、getDataBinderFactory

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
   Class<?> handlerType = handlerMethod.getBeanType();
   Set<Method> methods = this.initBinderCache.get(handlerType);
   if (methods == null) {
      methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
      this.initBinderCache.put(handlerType, methods);
   }
   List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
   this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
      if (clazz.isApplicableToBeanType(handlerType)) {
         Object bean = clazz.resolveBean();
         for (Method method : methodSet) {
            initBinderMethods.add(createInitBinderMethod(bean, method));
         }
      }
   });
   for (Method method : methods) {
      Object bean = handlerMethod.getBean();
      initBinderMethods.add(createInitBinderMethod(bean, method));
   }
   return createDataBinderFactory(initBinderMethods);
}
      
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
      throws Exception {

   return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}      

   這裡主要是就是将@InitBinder注解的的方法,不管是前面的ControllerAdvice注解添加的cache中的(initBinderAdviceCache)),還是目前handlerType本身有這個注解,都建立對應的InvocableHandlerMethod對象,通過createInitBinderMethod方法。

  8、createInitBinderMethod

private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {
   InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method);
   if (this.initBinderArgumentResolvers != null) {
      binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers);
   }
   binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer));
   binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
   return binderMethod;
}      

  這裡就是建立binderFactory的内容(ServletRequestDataBinderFactory)。

  9、getModelFactory

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
   SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
   Class<?> handlerType = handlerMethod.getBeanType();
   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((clazz, methodSet) -> {
      if (clazz.isApplicableToBeanType(handlerType)) {
         Object bean = clazz.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);
}
public ModelFactory(@Nullable List<InvocableHandlerMethod> handlerMethods,
      WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {

   if (handlerMethods != null) {
      for (InvocableHandlerMethod handlerMethod : handlerMethods) {
         this.modelMethods.add(new ModelMethod(handlerMethod));
      }
   }
   this.dataBinderFactory = binderFactory;
   this.sessionAttributesHandler = attributeHandler;
}      

  這裡與上面WebDataBinderFactory 類似,不過這裡是使用就是MODEL_ATTRIBUTE_METHODS這個MethodFilter。例如目前handlerMethod對應handlerType對應的@RequestMapping、@ModelAttribute注解(這個前置知識裡面有分析),然後去建立ModelFactory。

  10、createModelAttributeMethod

private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) {
   InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method);
   if (this.argumentResolvers != null) {
      attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
   }
   attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
   attrMethod.setDataBinderFactory(factory);
   return attrMethod;
}      

   這裡也是将這些method建立為InvocableHandlerMethod。我們再回到前面的invokeHandlerMethod方法。

  11、createInvocableHandlerMethod

protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
   return new ServletInvocableHandlerMethod(handlerMethod);
}      

  這裡是将目前要處理的HandlerMethod轉化為ServletInvocableHandlerMethod。再之後就對其進行一些初始化設定argumentResolvers、returnValueHandlers。

  12、modelFactory.initModel

    這些都處理後之後就是initModel

public void initModel(NativeWebRequest request, ModelAndViewContainer container,
      HandlerMethod handlerMethod) throws Exception {

   Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
   container.mergeAttributes(sessionAttributes);
   invokeModelAttributeMethods(request, container);
   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);
      }
   }
}      

   這裡主要是invokeModelAttributeMethods,去執行modelMethods

 13、invokeModelAttributeMethods

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

   while (!this.modelMethods.isEmpty()) {
      InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
      ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
      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);
         }
      }
   }
}
boolean binding() default true;
public boolean containsAttribute(String name) {
   return getModel().containsAttribute(name);
}      

    要更好的了解整個地方,可以去看下前面寫的這篇文章的demo部分,這裡就不多贅叙了。這裡再加一種情況來說明整個情況,就是有傳回的:

@ModelAttribute
public StudentVo modelAttributeMethodResult(@RequestParam("age") Integer age
        , @RequestParam("type") Short type, Model model)
{    
    StudentVo studentVo = new StudentVo();
    studentVo.setAge(age);
    return studentVo;
}      

這裡如果主動設定binding為false,如果目前ModelAttribute注解的name在container的model中有,将其設定到container的Model中,直接return。如果還沒有,就進行下面的invokeForRequest方法直接執行該方法。

   我們想看前一篇的demo在這裡的設定:

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

   這裡是傳回void,是以沒有下面的操作。下面來看執行的第二個方法,有傳回的:

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

   擷取預設名稱,如果沒有,将其添加的model中,供以後去擷取

SpringMVC源碼解讀 --- 處理器擴充卡(HandlerAdapter)的結構及源碼分析   1、HandlerAdapter接口:  2、SimpleControllerHandlerAdapter 3、SimpleServletHandlerAdapter 4、HttpRequestHandlerAdapter5、RequestMappingHandlerAdapter      總結

   這裡的initModel方法完成後,就是AsyncWebRequest,這個是與異步相關的,SpringMVC不會主動去處理,這裡就先跳過。

  14、invocableMethod.invokeAndHandle

    這個方法也在前面文章的前置知識點有單獨提取處理,這個就是去執行MethodHandler的,這裡就不再過多贅述。

  15、getModelAndView

     執行完MethodHandler後,就是去擷取ModelAndView

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);
   if (!mavContainer.isViewReference()) {
      mav.setView((View) mavContainer.getView());
   }
   if (model instanceof RedirectAttributes) {
      Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
      HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
      RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
   }
   return mav;
}      

  這裡主要是兩個點,一個isRequestHandled,這個我們再以前梳理HandlerMapping的時候有設定,現在可以知道這個設定是幹什麼的了,就是表示不需要再往下,去處理與View相關的内容了(簡單來說就是不需要使用ModelAndView )。第二個就是通過mavContainer去擷取建立ModelAndView。

      總結

    至此,關于HandlerAdapter的的父子結構與源碼梳理就完成了。