首先我們直接看其結構圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL4dGROVzYU1UMJpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5UzM3UDMwIjM3EDOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
可以看到在這個類其是有繼承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:
通過這張圖檔我們就可以清晰的這個這個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對方法的放回結果進行處理。