美文网首页spring源码
springMvc源码之@RequestParam参数绑定解析

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

作者: 后来丶_a24d | 来源:发表于2018-11-21 21:20 被阅读0次

    问题点

    • 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

    相关文章

      网友评论

        本文标题:springMvc源码之@RequestParam参数绑定解析

        本文链接:https://www.haomeiwen.com/subject/jrdhqqtx.html