springMvc源码之@RequestParam参数绑定解析

问题点

  • Http 调用接口的时候请求参数(String类型)由于有一个值为null, 而需要绑定的参数为Integer类型故一直404
  • 在类型转时调用了Integer.valueOf(str), 而从底层源码来看当传入参数为空时会直接抛出throw new NumberFormatException("null");

解决方案

  • 处理掉请求参数的null值并找到为null的原因。

源码简化版时序图

  • springmvc参数转换以及controller调用


    springmvc参数转换以及controller调用.jpg
  • springmvc绑定参数处理 从1.1开始


    springmvc绑定参数处理.jpg

对标spring源码

  • 从DispatchServlet的doService()开始分析

DispatcherServlet

  • 在doService方法中调用doDispatch方法,handlerAdapter开始调用controller中对应方法。
 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

RequestMappingHandlerAdapter

  • 这个类是具体的处理url请求HandlerAdapter,其中调用到handleInternal()方法
  • 继续调用invokeHandleMethod方法
return this.invokeHandleMethod(request, response, handlerMethod);
  • 继续调用下一个类的invokeAndHandle()方法

ServletInvocableHandlerMethod[图片上传中...(springmvc绑定参数处理.jpg-7d2cfe-1548496536396-0)]

  • 继承自InvocableHandlerMethod
  • 查看对应的invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        //下一个调用口, 父类的方法
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        this.setResponseStatus(webRequest);
        if (returnValue == null) {
            if (this.isRequestNotModified(webRequest) || this.hasResponseStatus() || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(this.responseReason)) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);

        try {
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception var6) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace(this.getReturnValueHandlingErrorMessage("Error handling return value", returnValue), var6);
            }

            throw var6;
        }
    }

InvocableHandlerMethod

  • 上一个类调用父类的invokeForRequest方法, 在这个方法中解决完参数绑定问题之后调用对应方法
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
                //下一个调用的方法 解决参数绑定的形参
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            StringBuilder sb = new StringBuilder("Invoking [");
            sb.append(getBeanType().getSimpleName()).append(".");
            sb.append(getMethod().getName()).append("] method with arguments ");
            sb.append(Arrays.asList(args));
            logger.trace(sb.toString());
        }
//反射调用
        Object returnValue = doInvoke(args);
        if (logger.isTraceEnabled()) {
            logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
        }
        return returnValue;
    }
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);
            GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());
            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.isTraceEnabled()) {
                        logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
                    }
                    throw ex;
                }
            }
            if (args[i] == null) {
                String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
                throw new IllegalStateException(msg);
            }
        }
        return args;
    }

HandlerMethodArgumentResolverComposite

  • 继承自HandlerMethodArgumentResolver
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
//下一个要调用的方法
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }

AbstractNamedValueMethodArgumentResolver

  • 继承自HandlerMethodArgumentResolver
  • 用于从指定值解析方法参数的抽象基类. Request parameters, request headers, and path variables这些
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        Class<?> paramType = parameter.getParameterType();
        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);

        Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
        if (arg == null) {
            if (namedValueInfo.defaultValue != null) {
                arg = resolveDefaultValue(namedValueInfo.defaultValue);
            }
            else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
                handleMissingValue(namedValueInfo.name, parameter);
            }
            arg = handleNullValue(namedValueInfo.name, arg, paramType);
        }
        else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
            arg = resolveDefaultValue(namedValueInfo.defaultValue);
        }

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
                      //下一个要调用的方法
            arg = binder.convertIfNecessary(arg, paramType, parameter);
        }

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

        return arg;
    }

DataBinder

  • implements PropertyEditorRegistry, TypeConverter
  • 数据绑定,把HttpServletRequest中的数据纳入到Spring的管理。
  • 将参数类型和请求数据再发给TypeConverter,由TypeConverter装配成一个bean。
  • TypeConverter将装配好的bean返回给DataBinder
    DataBinder将装配bean交给处理请求的方法。
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
            throws TypeMismatchException {
                  
        return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
    }

TypeConverterSupport

  • 类型转换的实现,持有一个TypeConverterDelegate,具体转换工作交给TypeConverterDelegate完成.
  • extends PropertyEditorRegistrySupport implements TypeConverter
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
            throws TypeMismatchException {

        return doConvert(value, requiredType, methodParam, null);
    }
private <T> T doConvert(Object value, Class<T> requiredType, MethodParameter methodParam, Field field)
            throws TypeMismatchException {
        try {
//下一个
            if (field != null) {
                return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
            }
            else {
                return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
            }
        }
        catch (ConverterNotFoundException ex) {
            throw new ConversionNotSupportedException(value, requiredType, ex);
        }
        catch (ConversionException ex) {
            throw new TypeMismatchException(value, requiredType, ex);
        }
        catch (IllegalStateException ex) {
            throw new ConversionNotSupportedException(value, requiredType, ex);
        }
        catch (IllegalArgumentException ex) {
            throw new TypeMismatchException(value, requiredType, ex);
        }
    }

TypeConverterDelegate

  • 单独的类
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam)
            throws IllegalArgumentException {

        return convertIfNecessary(null, null, newValue, requiredType,
                (methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
    }
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
            Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {

        Object convertedValue = newValue;

        // Custom editor for this type?
        PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

        ConversionFailedException firstAttemptEx = null;

        // No custom editor but custom ConversionService specified?
        ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) {
            TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
            TypeDescriptor targetTypeDesc = typeDescriptor;
            if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
                try {
//下一个
                    return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
                }
                catch (ConversionFailedException ex) {
                    // fallback to default conversion logic below
                    firstAttemptEx = ex;
                }
            }
        }

        。。。。

GenericConversionService

  • implements ConfigurableConversionService
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        Assert.notNull(targetType, "targetType to convert to cannot be null");
        if (sourceType == null) {
            Assert.isTrue(source == null, "source must be [null] if sourceType == [null]");
            return handleResult(null, targetType, convertNullSource(null, targetType));
        }
        if (source != null && !sourceType.getObjectType().isInstance(source)) {
            throw new IllegalArgumentException("source to convert from must be an instance of " +
                    sourceType + "; instead it was a " + source.getClass().getName());
        }
        GenericConverter converter = getConverter(sourceType, targetType);
        if (converter != null) {
//下一个
            Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
            return handleResult(sourceType, targetType, result);
        }
        return handleConverterNotFound(source, sourceType, targetType);
    }

ConversionUtils

public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
            TypeDescriptor targetType) {
        try {
            return converter.convert(source, sourceType, targetType);
        }
        catch (ConversionFailedException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new ConversionFailedException(sourceType, targetType, source, ex);
        }
    }

GenericConversionService的内部类ConverterFactoryAdapter中convert

public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            if (source == null) {
                return convertNullSource(sourceType, targetType);
            }
            return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
        }

StringToNumberConverterFactory的内部StringToNumberConverterFactory

- implements ConverterFactory<String, Number>

public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumber<T>(targetType);
    }
public T convert(String source) {
            if (source.length() == 0) {
                return null;
            }
            return NumberUtils.parseNumber(source, this.targetType);
        }

NumberUtils

public static <T extends Number> T parseNumber(String text, Class<T> targetClass) {
        Assert.notNull(text, "Text must not be null");
        Assert.notNull(targetClass, "Target class must not be null");
        String trimmed = StringUtils.trimAllWhitespace(text);

        if (targetClass.equals(Byte.class)) {
            return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed));
        }
        else if (targetClass.equals(Short.class)) {
            return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed));
        }
        else if (targetClass.equals(Integer.class)) {
            return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed));
        }
        else if (targetClass.equals(Long.class)) {
            return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed));
        }
        else if (targetClass.equals(BigInteger.class)) {
            return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed));
        }
        else if (targetClass.equals(Float.class)) {
            return (T) Float.valueOf(trimmed);
        }
        else if (targetClass.equals(Double.class)) {
            return (T) Double.valueOf(trimmed);
        }
        else if (targetClass.equals(BigDecimal.class) || targetClass.equals(Number.class)) {
            return (T) new BigDecimal(trimmed);
        }
        else {
            throw new IllegalArgumentException(
                    "Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]");
        }
    }

参考文章

  1. https://www.cnblogs.com/wewill/p/5676920.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,605评论 18 399
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,938评论 6 13
  • 一. Java基础部分.................................................
    wy_sure阅读 3,808评论 0 11
  • 一个运营的成长可从2个维度来考察 a:其一是可量化的能力评估模型 b:其二是可细分的成长路径 P1:纯新人,刚刚入...
    夏目与风阅读 1,603评论 0 0
  • 学霸这个词大家肯定都知道,顾名思义就是分数很高的学生,可学霸是怎样养成的呢?这倒成了很多人的疑惑。 学习分很多种方...
    茉瞳阅读 446评论 0 0