Spring HandlerMethodArgumentResolver是如何赋值的

刚接触Spring MVC的时候,第一感觉是神奇,尤其是如下方法:

@GetMapping("test")
public String test(HttpServletRequest request, HttpServletResponse response,ModelMap model,RedirectAttributes ra) {
    return "test";
}

这里面的HttpServletRequestHttpServletResponse等参数的值是从哪里来的?它们有没有固定的顺序?

对Spring MVC有一些了解后,回头再来看这些疑问,要想知道这些参数是怎么被赋值的,就得知道这个test方法是怎么被调用的,在spring mvc中,这些被RequestMapping标记的方法会被包装成HandlerMethod,具体可以看下AbstractHandlerMethodMapping的detectHandlerMethods方法,因此这个test方法调用逻辑就放到了对HandlerMethod的处理中,那么是谁对HandlerMethod进行了处理?

回到DispatcherServlet的doDispatch方法,

mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

发现它有一个getHander的方法,一旦被匹配到test请求,RequestMappingHandlerMapping就会返回一个HandlerExecutionChain,而HandlerExecutionChain的handler就是通过test方法包装成的HandlerMethod,那么最终处理这个HandlerMethod的,应该就是getHandlerAdapter返回的HandlerAdapter(RequestMappingHandlerAdapter),这样目光就放到了RequestMappingHandlerAdapter的处理中,通过查看它的方法,发现有个invokeHandlerMethod方法,故名思意,就是调用invokeHandlerMethod的,再看下invokeHandlerMethod方法,发现HandlerMethod被再次封装成了ServletInvocableHandlerMethod

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

ServletInvocableHandlerMethod应该同时负责处理返回值的工作。再往下看可以发现,是通过

invocableMethod.invokeAndHandle(webRequest, mavContainer);

这个方法对HandlerMethod进行处理的,再跟踪下去,发现了下面的方法:

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

至此,已经完成了对test方法的调用,毫无疑问,参数赋值应该就是在invokeForRequest方法中了,再看下该方法的代码,终于发现了获取参数值的方法:

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

定位到getMethodArgumentValues方法,总算看到了HandlerMethodArgumentResolver的身影:

if (this.argumentResolvers.supportsParameter(parameter)) {
    try {
        args[i] = this.argumentResolvers.resolveArgument(
                parameter, mavContainer, request, this.dataBinderFactory);
        continue;
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
        }
        throw ex;
    }
}

这样总算明白,spring mvc是根据参数的类型,来选择不同的HandlerMethodArgumentResolver,然后根据调用HandlerMethodArgumentResolver的resolveArgument方法,来返回实际的参数值,通过对HandlerMethodArgumentResolver实现类查看,就会知道HttpServletRequestHttpServletResponse其实是通过ServletRequestMethodArgumentResolverServletResponseMethodArgumentResolver来取值的。

最后,如果test方法后面加了个Integer参数,变成了这样:

@GetMapping("test")
public String test(HttpServletRequest request, HttpServletResponse response,ModelMap model,RedirectAttributes ra,Integer count) {
    return "test";
}

那么这个count参数又是通过哪个HandlerMethodArgumentResolver来取值的呢😄?