Springboot之HandlerMethodArgumentResolver源码解析

本文深入探讨了SpringBoot 2.0.5.RELEASE版本中参数解析的工作原理,详细分析了RequestMappingHandlerAdapter如何利用HandlerMethodArgumentResolver进行参数解析,并介绍了DispatcherServlet的doDispatch方法中如何调用这些解析器。

    Springboot版本2.0.5.release。

    前一篇博客讲解了如何用自定义HandlerMethodArgumentResolver进行参数解析,现在来看看springmvc是如何实现这个的。

    来看下RequestMappingHandlerAdapter,

      

                                                               图1

    HandlerMethodArgumentResolver是个接口,只有俩个方法,supportsParameter返回true标示支持解析,之后调用resolveArgument将结果作为参数传入,如下List-1所示。

    List-1

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);

    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}

    RequestMappingHandlerAdapter中有个属性argumentResolvers,是HandlerMethodArgumentResolverComposite,由类名称可以看出使用了组合模式。RequestMappingHandlerAdapter的getDefaultArgumentResolvers,这些是默认的参数处理器。

    List-2

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}

    如下图2所示:

 

                                                              图2

    DispatcherServlet的doDispatch方法中,间接调用ServletInvocableHandlerMethod的invokeAndHandle方法,之后会逐个调用HandlerMethodArgumentResolver。所以我们自定义的HandlerMethodArgumentResolver在图2中的步骤9和步骤10处处理。如下List-3所示,遍历所有的参数,对每个参数调用HandlerMethodArgumentResolverComposite的support和resolve方法,HandlerMethodArgumentResolverComposite再委托给各个HandlerMethodArgumentResolver处理。

    List-3

private Object[] getMethodArgumentValues(NativeWebRequest request, 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;
            }
            catch (Exception ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
                }
                throw ex;
            }
        }
        if (args[i] == null) {
            throw new IllegalStateException("Could not resolve method parameter at index " +
                    parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
                    ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
        }
    }
    return args;
}

    值得注意的是图2中的步骤12,HandlerMethodReturnValueHandler可以改变controller返还的值。万一步骤7抛出异常,那么HandlerMethodReturnValueHandler的步骤就不会执行,所以HandlerMethodReturnValueHandler处理不了业务代码抛出异常的情况。

Reference

  1. 源码https://github.com/spring-projects/spring-boot/tree/v2.0.5.RELEASE

转载于:https://my.oschina.net/u/2518341/blog/3083821

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值