SpringMVC参数解析器源码分析(二)--HandlerMethodArgumentResolver

HandlerMethodArgumentResolverComposite

简介

HandlerMethodArgumentResolverComposite组装了所有的参数解析器的链表,保存了springMVC提供的所有的参数解析器以及自定义的参数解析器,采用职责链的模式来完成参数解析器的查找,并完成参数解析生成目标对象。

源码分析

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

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

//参数解析器列表
private final List<HandlerMethodArgumentResolver> argumentResolvers =
        new LinkedList<HandlerMethodArgumentResolver>();

//缓存已经查找过的参数解析器 
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
        new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);


public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) {
    this.argumentResolvers.add(resolver);
    return this;
}


public HandlerMethodArgumentResolverComposite addResolvers(HandlerMethodArgumentResolver... resolvers) {
    if (resolvers != null) {
        for (HandlerMethodArgumentResolver resolver : resolvers) {
            this.argumentResolvers.add(resolver);
        }
    }
    return this;
}


public HandlerMethodArgumentResolverComposite addResolvers(List<? extends HandlerMethodArgumentResolver> resolvers) {
    if (resolvers != null) {
        for (HandlerMethodArgumentResolver resolver : resolvers) {
            this.argumentResolvers.add(resolver);
        }
    }
    return this;
}

//获取参数解析器列表
public List<HandlerMethodArgumentResolver> getResolvers() {
    return Collections.unmodifiableList(this.argumentResolvers);
}


public void clear() {
    this.argumentResolvers.clear();
}


//判断参数解析器是否支持参数解析
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return (getArgumentResolver(parameter) != null);
}

//参数解析器解析parameter,具体解析过程要在不同的解析器中详细描述
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, 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);
}

/**
 * 获取支持parameter的参数解析器,并保存到缓存中
 */
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
                        parameter.getGenericParameterType() + "]");
            }
            if (methodArgumentResolver.supportsParameter(parameter)) {
                result = methodArgumentResolver;
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}

}

HandlerMethodArgumentResolver

简述

下面来我们来看看常用的HandlerMethodArgumentResolver实现类:

  1. RequestParamMethodArgumentResolver支持有@RequestParam注解的参数或带有MultipartFile类型的参数

  2. RequestParamMapMethodArgumentResolver支持有@RequestParam注解的参数并且参数类型是实现Map接口的属性

  3. PathVariableMethodArgumentResolver支持有@PathVariable注解的参数

  4. RequestResponseBodyMethodProcessor 支持有@RequestBody注解的参数

  5. ServletRequestMethodArgumentResolver参数类型是实现或继承或是WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、Locale、TimeZone、InputStream、Reader、HttpMethod这些类。(这就是为何我们在Controller中的方法里添加一个HttpServletRequest参数,Spring会为我们自动获得HttpServletRequest对象的原因)

  6. ModelAttributeMethodProcessor 支持@ModelAttribute注解参数

RequestParamMethodArgumentResolver

RequestParamMethodArgumentResolver支持两种参数解析,一种是含@RequestParam注解的参数,另一种就是简单类型,如Integer、String、Date、URI, URL,Locale等。

使用场景
public String getUser(@RequestParameter("id") Long id) {
    return userService.get(id);
}


public String getUser(Long id) {
    return userService.get(id);
}
supportsParameter 判断是否支持当前parameter类型
public boolean supportsParameter(MethodParameter parameter) {
    //方法的参数中是否有RequestParam注解
    if (parameter.hasParameterAnnotation(RequestParam.class)) {
        //参数是否是Map  
        if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
            String paramName = parameter.getParameterAnnotation(RequestParam.class).name();
            return StringUtils.hasText(paramName);
        }
        else {
            return true;
        }
    }
    else {
        //如果参数上有RequestPart注解,直接返回faslse  
        if (parameter.hasParameterAnnotation(RequestPart.class)) {
            return false;
        }
        parameter = parameter.nestedIfOptional();
        //是否是文件上传
        if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
            return true;
        }
        //useDefaultResolution为true的bean是用来处理简单类型的bean的
        else if (this.useDefaultResolution) {
            return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
        }
        else {
            return false;
        }
    }
}

argumentResolvers中有两个RequestParamMethodArgumentResolver bean,一个useDefaultResolution为false,
一个useDefaultResolution为true。下面看一下哪些是简单的类型:

public static boolean isSimpleProperty(Class<?> clazz) {
    Assert.notNull(clazz, "Class must not be null");
    return isSimpleValueType(clazz) || (clazz.isArray() && isSimpleValueType(clazz.getComponentType()));
}

public static boolean isSimpleValueType(Class<?> clazz) {
    return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
            CharSequence.class.isAssignableFrom(clazz) ||
            Number.class.isAssignableFrom(clazz) ||
            Date.class.isAssignableFrom(clazz) ||
            URI.class == clazz || URL.class == clazz ||
            Locale.class == clazz || Class.class == clazz);
}

也就是我们的请求的参数类型为:枚举类型、String类型、Long、Integer、Float、Byte、Short、Double、Date、URI、URL、Locale、Class、文件上传对象或者参数是数组,数组类型为上面列出的类型时,则参数解析器为:RequestParamMethodArgumentResolver。

解析参数
resolveArgument

在RequestParamMethodArgumentResolver类中没有找到对应的resolveArgument方法,但是我们在他的父类AbstractNamedValueMethodArgumentResolver中找到了resolveArgument这个方法,源码如下:

public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        //获取入参的名称信息
        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
        MethodParameter nestedParameter = parameter.nestedIfOptional();

        Object resolvedName = resolveStringValue(namedValueInfo.name);
        if (resolvedName == null) {
            throw new IllegalArgumentException(
                    "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
        }
        //解析参数值  
        Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
        if (arg == null) {//request请求参数值为null
            //判断是否存在默认值
            if (namedValueInfo.defaultValue != null) {
                arg = resolveStringValue(namedValueInfo.defaultValue);
            }
            //判断添加@RequestParam的参数字段是否必须传值,默认必传
            else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                //参数缺失处理,抛出相应类型异常
                handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
            }
            //null值处理
            arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
        }
        else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }

        //参数类型转换
        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
            try {
                arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
            }
            catch (ConversionNotSupportedException ex) {
                throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                        namedValueInfo.name, parameter, ex.getCause());
            }
            catch (TypeMismatchException ex) {
                throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                        namedValueInfo.name, parameter, ex.getCause());

            }
        }

        handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

        return arg;
    }
获取NamedValueInfo对象
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
    NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
    if (namedValueInfo == null) {//缓存中不存在
        ////创建NamedValueInfo对象  
        namedValueInfo = createNamedValueInfo(parameter);
        //更新NamedValueInfo对象 
        namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
        //缓存
        this.namedValueInfoCache.put(parameter, namedValueInfo);
    }
    return namedValueInfo;
}

RequestParamMethodArgumentResolver中的createNamedValueInfo方法以及RequestParamNamedValueInfo类:

通过RequestParamNamedValueInfo的两个构造函数可知,当@RequestParam存在的时候,使用注解的值,否则取默认的值。

protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
    RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
    return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}

private static class RequestParamNamedValueInfo extends NamedValueInfo {

    public RequestParamNamedValueInfo() {
        super("", false, ValueConstants.DEFAULT_NONE);
    }

    public RequestParamNamedValueInfo(RequestParam annotation) {
        super(annotation.name(), annotation.required(), annotation.defaultValue());
    }
}

updateNamedValueInfo方法可以看到,如果参数没有注解@RequestParam的话,则从MethodParameter中解析出参数的名字

private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
    String name = info.name;
    if (info.name.length() == 0) {
        //从MethodParameter中解析出参数的名字 
        name = parameter.getParameterName();
        if (name == null) {
            throw new IllegalArgumentException(
                    "Name for argument type [" + parameter.getNestedParameterType().getName() +
                    "] not available, and parameter name information not found in class file either.");
        }
    }
    //转换默认值
    String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
    return new NamedValueInfo(name, info.required, defaultValue);
}
解析参数值

protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {

    //获取HttpServletRequest  
    HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
    //判断是不是文件上传的请求 
    MultipartHttpServletRequest multipartRequest =
            WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
    //获取上传文件对象
    Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
    if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
        return mpArg;
    }

    Object arg = null;
    //判断是否是文件上传的请求
    if (multipartRequest != null) {
        //获取上传的文件列表
        List<MultipartFile> files = multipartRequest.getFiles(name);
        if (!files.isEmpty()) {
            arg = (files.size() == 1 ? files.get(0) : files);
        }
    }
    //其他请求都没返回,则是最简单的请求
    if (arg == null) {
        //获取参数值
        String[] paramValues = request.getParameterValues(name);
        if (paramValues != null) {
            arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
        }
    }
    return arg;
}

PathVariableMethodArgumentResolver

简单示例
public User getUser(@PathVariable Long id) {
    return userService.get(id);
}

通过类名便可知道是路径变量做参数的解析器,这个类与RequestParamMethodArgumentResolver一样是AbstractNamedValueMethodArgumentResolver的子类,因此主要不同的地方在supportsParameter与resolveName方法,下面简单看一下:

supportsParameter
public boolean supportsParameter(MethodParameter parameter) {
    //没有@PathVariable注解直接返回false
    if (!parameter.hasParameterAnnotation(PathVariable.class)) {
        return false;
    }
    //判断是否是Map参数
    if (Map.class.isAssignableFrom(parameter.getParameterType())) {
        String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
        return StringUtils.hasText(paramName);
    }
    return true;
}
resolveName
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
    Map<String, String> uriTemplateVars =
        (Map<String, String>) request.getAttribute(
                HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
    return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
}

ModelAttributeMethodProcessor

简单示例

public String addB(@ModelAttribute User user)throws Exception {
    userService.addA(user.getUser_name(),user.getAge());
    return "success";
}
supportsParameter

支持有@ModelAttribute注解或者不需要注解但是入参不是简单类型的情况下,支持ModelAttributeMethodProcessor参数解析器

public boolean supportsParameter(MethodParameter parameter) {
    return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
            (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
resolveArgument

这里参数以键值对的形式传入,从request取出后组成ProperValue列表,然后与参数对象的field对应赋值。具体绑定过程需要研究一下属性编辑器、数据绑定机制。

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

    String name = ModelFactory.getNameForParameter(parameter);
    Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
            createAttribute(name, parameter, binderFactory, webRequest));

    if (!mavContainer.isBindingDisabled(name)) {
        ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
        if (ann != null && !ann.binding()) {
            mavContainer.setBindingDisabled(name);
        }
    }

    WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    //参数绑定
    if (binder.getTarget() != null) {
        if (!mavContainer.isBindingDisabled(name)) {
            bindRequestParameters(binder, webRequest);
        }
        validateIfApplicable(binder, parameter);
        if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
        }
    }

    // Add resolved attribute and BindingResult at the end of the model
    Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}

RequestResponseBodyMethodProcessor

RequestResponseBodyMethodProcessor这个类其实同时实现了HandlerMethodReturnValueHandler和HandlerMethodArgumentResolver这两个接口。我们在当前文章中只介绍参数解析的部分。

supportsParameter
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestBody.class);
}
resolveArgument

这里不在详细介绍,具体的转换可以看消息转换器的源码。

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

    parameter = parameter.nestedIfOptional();
    //具体处理是使用消息转换器
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);

    WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
    if (arg != null) {
        validateIfApplicable(binder, parameter);
        if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
        }
    }
    mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

    return adaptArgumentIfNecessary(arg, parameter);
}

总结

参数转换的方法不仅仅这些,这里只是选取了几个比较常用的参数解析器简单的介绍了一下,有些直接在类中就完成了解析;但是有些还需要借助其他的类才能完成解析。

扩展

职责链设计模式,需要写一篇相关文章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值