天天看點

SpringMVC源碼解讀 --- 處理器擴充卡 - 2 源碼解讀前置知識ServletInvocableHandlerMethod類父子結構及源碼分析    1、HandlerMethod類2、InvokeHandlerMethod3、ServletInvocableHandlerMethod

      首先我們直接看其結構圖:

SpringMVC源碼解讀 --- 處理器擴充卡 - 2 源碼解讀前置知識ServletInvocableHandlerMethod類父子結構及源碼分析    1、HandlerMethod類2、InvokeHandlerMethod3、ServletInvocableHandlerMethod

可以看到在這個類其是有繼承HandlerMethod的,就是我們講的用于描叙被@RequestMapping注解的方法。

    1、HandlerMethod類

    1、成員變量

public class HandlerMethod {

   protected final Log logger = LogFactory.getLog(getClass());
   private final Object bean;
   @Nullable
   private final BeanFactory beanFactory;
   private final Class<?> beanType;
   private final Method method;
   private final Method bridgedMethod;
   private final MethodParameter[] parameters;
   @Nullable
   private HttpStatus responseStatus;
   @Nullable
   private String responseStatusReason;
   @Nullable
   private HandlerMethod resolvedFromHandlerMethod;
        ............
}
           

  可以看到這個方法的全局變量,其是用來描叙Method,這個Method的參數描叙MethodParameter、以及橋方法、這個Method對應的Bean(這個Method所屬的對象),這裡将beanFactory也有設定。

    我們再通過一個demo來看下這個:

@RequestMapping(value = "methodHandler",method = RequestMethod.GET)
public String  methodHandler(@RequestParam("age") Integer age,
                               @RequestParam("name") String name,@RequestParam("type") Short type)
{
    System.out.println(".....methodHandler.....");
    return ".....methodHandler.....";
}
           

  然後看通過getHandler方法擷取到的Handler:

SpringMVC源碼解讀 --- 處理器擴充卡 - 2 源碼解讀前置知識ServletInvocableHandlerMethod類父子結構及源碼分析    1、HandlerMethod類2、InvokeHandlerMethod3、ServletInvocableHandlerMethod

  通過這張圖檔我們就可以清晰的這個這個HandlerMethod是描叙什麼的,就不再過多贅叙了。

   2、方法

private void evaluateResponseStatus() {
   ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
   if (annotation == null) {
      annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
   }
   if (annotation != null) {
      this.responseStatus = annotation.code();
      this.responseStatusReason = annotation.reason();
   }
}
           

其主要是這個方法這個就是看目前的方法有沒有@ResponseStatus注解,如果有,就将其設定到responseStatus(這個後面會有應用),然後在調用方法的時候再将這個status設定到response中,這個方法是私有方法,其的調用是在構造函數中:   

public HandlerMethod(Object bean, Method method) {
   Assert.notNull(bean, "Bean is required");
   Assert.notNull(method, "Method is required");
   this.bean = bean;
   this.beanFactory = null;
   this.beanType = ClassUtils.getUserClass(bean);
   this.method = method;
   this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
   this.parameters = initMethodParameters();
   evaluateResponseStatus();
}
           

      看了這個基礎的HandlerMethod,我們再來看下InvokeHandlerMethod

2、InvokeHandlerMethod

   1、成員變量 

public class InvocableHandlerMethod extends HandlerMethod {

   @Nullable
   private WebDataBinderFactory dataBinderFactory;

   private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();

   private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
  ..............
}
           

  1、WebDataBinderFactory

  這個WebDataBinderFactory 就是去建立WebDataBinder

public interface WebDataBinderFactory {
   WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
         throws Exception;
}
           

然後這個WebDataBinder主要是與@InitBinder注解相關的,然後這個WebDataBinder其是與字段類型轉換,以及進行相關屬性設定有關,關于WebDataBinder與WebDataBinderFactory 的類結構後面再梳理。

  2、HandlerMethodArgumentResolverComposite

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

   protected final Log logger = LogFactory.getLog(getClass());

   private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();

   private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
         new ConcurrentHashMap<>(256);
    ...............
}      

       可以看到這個類繼承HandlerMethodArgumentResolver接口,同時是放HandlerMethodArgumentResolver的(argumentResolvers ),這個HandlerMethodArgumentResolver接口可以看上一篇的梳理

  2、方法

  1、invokeForRequest

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
   Object returnValue = doInvoke(args);
   return returnValue;
}      

  是以對于request請求對應@RequestMapping注解的方法(HandlerMethod、Handler)的執行就是在這裡,(providedArgs是幹嘛的?後面再解答)。

2、getMethodArgumentValues

private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   MethodParameter[] parameters = getMethodParameters();
   Object[] args = new Object[parameters.length];
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      args[i] = resolveProvidedArgument(parameter, providedArgs);
      if (args[i] != null) {
         continue;
      }
      if (this.argumentResolvers.supportsParameter(parameter)) {
         try {
            args[i] = this.argumentResolvers.resolveArgument(
                  parameter, mavContainer, request, this.dataBinderFactory);
            continue;
         }
          ..........
      if (args[i] == null) {
         throw new IllegalStateException("Could not resolve method parameter at index " +
           parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +
         ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
       }
   }
   return args;
}
public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
   this.parameterNameDiscoverer = parameterNameDiscoverer;
}      

    這個方法就是對請求方法的參數進行解析及擷取,實作是擷取這個方法需要哪些參數MethodParameter,再周遊擷取。

3、resolveProvidedArgument

@Nullable
private Object resolveProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
   if (providedArgs == null) {
      return null;
   }
   for (Object providedArg : providedArgs) {
      if (parameter.getParameterType().isInstance(providedArg)) {
         return providedArg;
      }
   }
   return null;
}      

  這裡就解釋了providedArgs是幹什麼用的,如果方法的參數類型與providedArgs的類型時一樣的,就直接傳回對應providedArgs的值,不再需要進行測試解析了(這裡目前找到的再SpringMVC中的使用主要是對WebDataBinder的使用(@InitBinder))。

args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
   continue;
}      

4、argumentResolvers.supportsParameter(HandlerMethodArgumentResolverComposite)

   這裡是調用的HandlerMethodArgumentResolverComposite的接口:

@Override
public boolean supportsParameter(MethodParameter parameter) {
   return (getArgumentResolver(parameter) != null);
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
   HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
   if (result == null) {
      for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
         if (methodArgumentResolver.supportsParameter(parameter)) {
            result = methodArgumentResolver;
            this.argumentResolverCache.put(parameter, result);
            break;
         }
      }
   }
   return result;
}      

是以這裡就是,就是通過supportsParameter判斷有沒有處理該方法參數對應的HandlerMethodArgumentResolver。

能找到再調用resolveArgument方法去進行對應參數的解析,不能擷取就報錯IllegalStateException。

5、argumentResolvers.resolveArgument(HandlerMethodArgumentResolverComposite )

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
   if (resolver == null) {
      throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
   }
   return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}      

  上面2-5的方法都是由getMethodArgumentValues方法的調用衍生出來的,先在這個方法就調用完成,已經擷取到要執行對應方法的入參了,現在再回到前面

6、doInvoke(args)

   這個方法就是對對應方法的執行了:

protected Object doInvoke(Object... args) throws Exception {
   ReflectionUtils.makeAccessible(getBridgedMethod());
   try {
      return getBridgedMethod().invoke(getBean(), args);
   }
   catch (IllegalArgumentException ex) {
      .........
      throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
   }
   catch (InvocationTargetException ex) {
        .........
   }
}      

3、ServletInvocableHandlerMethod

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
   .......
   @Nullable
   private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
       .........
}      

  1、成員變量

   這個我們可以看到主要是HandlerMethodReturnValueHandlerComposite

  然後這個HandlerMethodReturnValueHandlerComposite與前面HandlerMethodArgumentResolverComposite是類似的,隻是HandlerMethodArgumentResolver是放HandlerMethodArgumentResolver的,HandlerMethodReturnValueHandlerComposite是放HandlerMethodReturnValueHandler的。

   2、方法

      1、invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   setResponseStatus(webRequest);

   if (returnValue == null) {
      if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
         mavContainer.setRequestHandled(true);
         return;
      }
   }
   else if (StringUtils.hasText(getResponseStatusReason())) {
      mavContainer.setRequestHandled(true);
      return;
   }

   mavContainer.setRequestHandled(false);
   try {
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   }
   ...........
}      

可以看到這裡首先就是調用invokeForRequest(InvokeHandlerMethod)方法擷取執行對應方法的調用結果。然後setResponseStatus方法的調用,再通過isRequestNotModified方法(如果對應請求的内容沒有進行修改)、getResponseStatus(如果有設定response的status)、isRequestHandled(請求已經處理了),就設定mavContainer.setRequestHandled(true)再return。如果沒有return,然後再通過returnValueHandlers.handleReturnValue去處理傳回結果。

  2、setResponseStatus

private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
   HttpStatus status = getResponseStatus();
   if (status == null) {
      return;
   }
   HttpServletResponse response = webRequest.getResponse();
   if (response != null) {
      String reason = getResponseStatusReason();
      if (StringUtils.hasText(reason)) {
         response.sendError(status.value(), reason);
      }
      else {
         response.setStatus(status.value());
      }
   }

   // To be picked up by RedirectView
   webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}      

  可以看到這個方法就是看有沒有設定status(前面在HandlerMethod有梳理這個值的設定通過@ResponseStatus注解),如果沒有設定就直接return,如果有設定,就将對應HttpStatus設定到response中。

    最後我們總結下ServletInvocableHandlerMethod方法,這個類就是将被@RequestMapping注解的方法描叙就來,再通過方法去進行這個方法執行的準備(通過HandlerMethodArgumentResolver去進行方法對應參數的擷取及類型轉化),擷取到對應參數後,再通過invoke方法進行執行,執行後,再通過HandlerMethodResultResolver對方法的放回結果進行處理。