当前位置:   article > 正文

SpringMVC源码解读 --- 处理器适配器(HandlerAdapter)的结构及源码分析_spring adapter 目录结构

spring adapter 目录结构

  这一章我们主要是梳理下HandlerAdapter,在看这一篇的时候可以去看下前面的关于HandlerMapping与处理器映射器前置知识的内容,怎样能更好的理解。

   1、HandlerAdapter接口:

  1. public interface HandlerAdapter {
  2. boolean supports(Object handler);
  3. @Nullable
  4. ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
  5. long getLastModified(HttpServletRequest request, Object handler);
  6. }

    可以看到这个接口有三个方法。

   1、supports方法

         这个接口就是看这类是用来处理哪种handler的。

   2、handle方法

   这个方法就是具体的处理方法,通过前面的文章的梳理,可以看到Spring在进行选择的时候一般是通过supports方法判断选择哪个,然后再用handle方法去具体处理。

   3、getLastModified

       这个就是看这个handler最后的修改时间,,有些参数能在没有修改的时候不进行处理。

  下面我们来看下其的实现类

  2、SimpleControllerHandlerAdapter

  1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {
  2. @Override
  3. public boolean supports(Object handler) {
  4. return (handler instanceof Controller);
  5. }
  6. @Override
  7. @Nullable
  8. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  9. throws Exception {
  10. return ((Controller) handler).handleRequest(request, response);
  11. }
  12. @Override
  13. public long getLastModified(HttpServletRequest request, Object handler) {
  14. if (handler instanceof LastModified) {
  15. return ((LastModified) handler).getLastModified(request);
  16. }
  17. return -1L;
  18. }
  19. }

  这里可以看到,这个HandlerAdapter就是通过supports看下当前要处理的handler是不是Controller。如果是,就通过handle去处理。

 3、SimpleServletHandlerAdapter

  1. public class SimpleServletHandlerAdapter implements HandlerAdapter {
  2. @Override
  3. public boolean supports(Object handler) {
  4. return (handler instanceof Servlet);
  5. }
  6. @Override
  7. @Nullable
  8. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  9. throws Exception {
  10. ((Servlet) handler).service(request, response);
  11. return null;
  12. }
  13. @Override
  14. public long getLastModified(HttpServletRequest request, Object handler) {
  15. return -1;
  16. }
  17. }

 这个HandlerAdapter supports方法就是判断handler是不是Servlet,如果是就用handle去处理,直接调用Servlet的service方法。返回的为null。

 4、HttpRequestHandlerAdapter

  1. public class HttpRequestHandlerAdapter implements HandlerAdapter {
  2. @Override
  3. public boolean supports(Object handler) {
  4. return (handler instanceof HttpRequestHandler);
  5. }
  6. @Override
  7. @Nullable
  8. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  9. throws Exception {
  10. ((HttpRequestHandler) handler).handleRequest(request, response);
  11. return null;
  12. }
  13. @Override
  14. public long getLastModified(HttpServletRequest request, Object handler) {
  15. if (handler instanceof LastModified) {
  16. return ((LastModified) handler).getLastModified(request);
  17. }
  18. return -1L;
  19. }
  20. }

  可以看到这个Adapter是用来处理HttpRequestHandler的,handle调用的是其的handleRequest方法。这个HttpRequestHandler我的理解其实这个与Servlet是类似的(因为其入参一样是HttpServletRequest 、HttpServletResponse ),你可以自定义处理器去实现HttpRequestHandler这个接口。

     SpringMVC对HttpRequestHandler的实现一般会用到的应该是两个

  1、DefaultServletHttpRequestHandler

  1. public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
  2. ............
  3. @Override
  4. public void setServletContext(ServletContext servletContext) {
  5. this.servletContext = servletContext;
  6. if (!StringUtils.hasText(this.defaultServletName)) {
  7. if (this.servletContext.getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) {
  8. this.defaultServletName = COMMON_DEFAULT_SERVLET_NAME;
  9. }
  10. else if (this.servletContext.getNamedDispatcher(GAE_DEFAULT_SERVLET_NAME) != null) {
  11. this.defaultServletName = GAE_DEFAULT_SERVLET_NAME;
  12. }
  13. else if (this.servletContext.getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) {
  14. this.defaultServletName = RESIN_DEFAULT_SERVLET_NAME;
  15. }
  16. else if (this.servletContext.getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null) {
  17. this.defaultServletName = WEBLOGIC_DEFAULT_SERVLET_NAME;
  18. }
  19. else if (this.servletContext.getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null) {
  20. this.defaultServletName = WEBSPHERE_DEFAULT_SERVLET_NAME;
  21. }
  22. else {
  23. throw new IllegalStateException("Unable to locate the default servlet for serving static content. " +
  24. "Please set the 'defaultServletName' property explicitly.");
  25. }
  26. }
  27. }
  28. @Override
  29. public void handleRequest(HttpServletRequest request, HttpServletResponse response)
  30. throws ServletException, IOException {
  31. Assert.state(this.servletContext != null, "No ServletContext set");
  32. RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
  33. if (rd == null) {
  34. throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
  35. this.defaultServletName + "'");
  36. }
  37. rd.forward(request, response);
  38. }
  39. }

   这里有些tomcat的内容,这里先挖一个坑。

可以看到其是先通过setServletContext方法,看是哪种getNamedDispatcher,再将defaultServletName进行对应的设置,然后在通过handleRequest方法,获取到RequestDispatcher(tomcat)的类,再调用其的forward方法。

  2、ResourceHttpRequestHandler

   这个类看名称就是知道其是用来处理Resource的,这里简答看下其的handleRequest方法:

  1. @Override
  2. public void handleRequest(HttpServletRequest request, HttpServletResponse response)
  3. throws ServletException, IOException {
  4. Resource resource = getResource(request);
  5. ............
  6. MediaType mediaType = getMediaType(request, resource);
  7. ........
  8. ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
  9. if (request.getHeader(HttpHeaders.RANGE) == null) {
  10. setHeaders(response, resource, mediaType);
  11. this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
  12. }
  13. else {
  14. response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
  15. ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
  16. try {
  17. ...........
  18. this.resourceRegionHttpMessageConverter.write(
  19. HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage);
  20. }
  21. catch (IllegalArgumentException ex) {
  22. response.setHeader("Content-Range", "bytes */" + resource.contentLength());
  23. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  24. }
  25. }
  26. }

 可以看到,这里分为5个部分:

     1、通过request,获取对应的Resource。

     2、获取对应的MediaType。

     3、通过response构建outputMessage。

     4、设置response的头部。

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

  1、我们再通过一个demo来更直接的了解:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:context="http://www.springframework.org/schema/context"
  6. xmlns:mvc="http://www.springframework.org/schema/mvc"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  9. http://www.springframework.org/schema/context
  10. http://www.springframework.org/schema/context/spring-context-3.2.xsd
  11. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
  12. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
  13. <bean id="beanNameUrlHandlerMapping"
  14. class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
  15. </bean>
  16. <bean id="resourceHandler1" name="/ResourceHandlerHello.html"
  17. class="org.springframework.web.servlet.resource.ResourceHttpRequestHandler">
  18. <property name="locations">
  19. <list>
  20. <ref bean="myClassPathResource"/>
  21. </list>
  22. </property>
  23. </bean>
  24. <bean id="myClassPathResource" class="org.springframework.core.io.ClassPathResource">
  25. <constructor-arg name="path" value="static/"/>
  26. </bean>
  27. </beans>

  然后在maven项目的resource目录新建static目录,创建ResourceHandlerHello.html静态界面

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. Hello ResourceHttpRequestHandler
  9. </body>
  10. </html>

                                                     

 启动tomcat项目,访问http://localhost:8080/ResourceHandlerHello.html 地址就能返回界面:

           

         下面 我们来debug看下流程,前面获取到handler(resourceHandler1)我们就不再梳理了,可以看以前关于HandlerMapping的内容。这里会根据这个bean标签的name="/ResourceHandlerHello.html"将其注册到BeanNameUrlHandlerMapping中,然后在请求的时候就会将对应的ResourceHttpRequestHandler返回,然后会根据这个ResourceHttpRequestHandler(HttpRequestHandler)获取到对应的HttpRequestHandlerAdapter,我们现在来看下这个handler的handleRequest方法:

    1、先是getResource

然后这里的getLocations方法就是我们添加的 <ref bean="myClassPathResource"/>:

                   

可以看到这里之后就获取到这个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在这里的设置:

         

   这里是返回void,所以没有下面的操作。下面来看执行的第二个方法,有返回的:

   

   获取默认名称,如果没有,将其添加的model中,供以后去获取

   这里的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的的父子结构与源码梳理就完成了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/66267
推荐阅读
相关标签
  

闽ICP备14008679号