springboot系列--web相关知识探索五

news2024/11/25 5:26:41

一、前言

web相关知识探索四中研究了请求中所带的参数是如何映射到接口参数中的,也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。web相关知识探索四中主要研究了复杂参数底层绑定原理。本次主要是研究自定义对象参数数据绑定底层原理。

二、原理

一、测试用例 

@Data
public class Person {

    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;



    @Data
    public static class Pet {

        private String name;
        private String age;

    }

}




    @PostMapping("/test")
    public Person testEntity(Person person){
        System.out.println(JSONUtil.toJsonStr(person));
        return person;
    }

二、postman请求方式

三、原理

一、寻找参数解析器

请求进来以后,直接到匹配合适的参数解析器这一步,由于我们的参数只有一个,也就是Person类型的自定义对象参数。可以找到是这个参数处理器处理自定义JavaBean参数。

ServletModelAttributeMethodProcessor

// org.springframework.web.method.annotation包下的ModelAttributeMethodProcessor类里面的方法
// 判断当前参数,这个解析器支不支持解析
    public boolean supportsParameter(MethodParameter parameter) {
	// 判断条件为,是否使用了ModelAttribute这个注解,或者annotationNotRequired这个属性是否为true也就是这个注解不是必须得,且这个参数是不是简单类型
        return parameter.hasParameterAnnotation(ModelAttribute.class) || this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType());
    }
	
	
	// 简单类型判断标准
	    public static boolean isSimpleProperty(Class<?> type) {
        Assert.notNull(type, "'type' must not be null");
        return isSimpleValueType(type) || type.isArray() && isSimpleValueType(type.getComponentType());
    }


	// 数字,字符串,void、枚举等等
    public static boolean isSimpleValueType(Class<?> type) {
        return Void.class != type && Void.TYPE != type && (ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Temporal.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type);
    }

二、参数解析

一、请求参数到目标参数的数据类型转换,这部分只是获取转换器,到了准备转换数据类型步骤

// 开始进行参数解析
    @Nullable
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
        Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
        String name = ModelFactory.getNameForParameter(parameter);
        ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
        if (ann != null) {
            mavContainer.setBinding(name, ann.binding());
        }

        Object attribute = null;
        BindingResult bindingResult = null;
        if (mavContainer.containsAttribute(name)) {
            attribute = mavContainer.getModel().get(name);
        } else {
		// 这里以上都不用管,以上是使用ModelAttribute注解处理的逻辑
            try {
				// 创建一个空的参数对象,也就是接口参数里面的自定义javaBean对象,但是里面的属性值是空的。之后就会让这个对象的属性值与请求进来所带的参数进行一一绑定。
                attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
            } catch (BindException var10) {
                if (this.isBindExceptionRequired(parameter)) {
                    throw var10;
                }

                if (parameter.getParameterType() == Optional.class) {
                    attribute = Optional.empty();
                } else {
                    attribute = var10.getTarget();
                }

                bindingResult = var10.getBindingResult();
            }
        }

		// 如果没有请求进来的参数进行绑定,就会进入下面进行绑定
        if (bindingResult == null) {
			// 创建绑定器,这里会把原生请求对象以及空属性person对象封装进去。同时还有各种类型的转换器,
			// 例如,所有请求都是通过http超文本协议传过来的,所以所有请求参数都是文本类型也就是String,比如年龄12,请求进来就会把"12"字符串类型转为Integer类型
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) {
                if (!mavContainer.isBindingDisabled(name)) {
				// 这里就进行了参数绑定,也就是给Person绑定上了请求所携带的值。主要逻辑在里面
                    this.bindRequestParameters(binder, webRequest);
                }

                this.validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
                    throw new BindException(binder.getBindingResult());
                }
            }

            if (!parameter.getParameterType().isInstance(attribute)) {
                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
            }

            bindingResult = binder.getBindingResult();
        }

        Map<String, Object> bindingResultModel = bindingResult.getModel();
        mavContainer.removeAttributes(bindingResultModel);
        mavContainer.addAllAttributes(bindingResultModel);
        return attribute;
    }
	
	
	
		// 这里会调用父类的方法创建一个Perrson对象
	    protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        String value = this.getRequestValueForAttribute(attributeName, request);
        if (value != null) {
            Object attribute = this.createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
            if (attribute != null) {
                return attribute;
            }
        }
		// 会进到这里
        return super.createAttribute(attributeName, parameter, binderFactory, request);
    }
	
	
	
	
		// 具体创建一个person对象逻辑
	    protected Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
	   // 通过MethodParameter对象获取接口参数
	   MethodParameter nestedParameter = parameter.nestedIfOptional();
	   // 获取接口参数类型
        Class<?> clazz = nestedParameter.getNestedParameterType();
		// 获取参数类型对应的构造函数
        Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
        // 这里就是通过底层反射之类的构造Perrson对象
		Object attribute = this.constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
        if (parameter != nestedParameter) {
            attribute = Optional.of(attribute);
        }

        return attribute;
    }
	
	
	
	
	
	
		// 这里就进行了参数绑定,ServletModelAttributeMethodProcessor中的bindRequestParameters方法
	    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
		// 获取请求
        ServletRequest servletRequest = (ServletRequest)request.getNativeRequest(ServletRequest.class);
        Assert.state(servletRequest != null, "No ServletRequest");
        ServletRequestDataBinder servletBinder = (ServletRequestDataBinder)binder;
        // 这里进行了数据绑定
		servletBinder.bind(servletRequest);
    }
	
	
	
		// 对应上面的bind数据绑定方法,ServletRequestDataBinder类里面的
	    public void bind(ServletRequest request) {
		// 获取请求中参数的k-v数据对
        MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
        MultipartRequest multipartRequest = (MultipartRequest)WebUtils.getNativeRequest(request, MultipartRequest.class);
        if (multipartRequest != null) {
            this.bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
        } else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/")) {
            HttpServletRequest httpServletRequest = (HttpServletRequest)WebUtils.getNativeRequest(request, HttpServletRequest.class);
            if (httpServletRequest != null) {
                StandardServletPartUtils.bindParts(httpServletRequest, mpvs, this.isBindEmptyMultipartFiles());
            }
        }

        this.addBindValues(mpvs, request);
		// 这里是真正进行参数绑定的方法
        this.doBind(mpvs);
    }
	
	
	
	// WebDataBinder类里面的绑定方法
    protected void doBind(MutablePropertyValues mpvs) {
        this.checkFieldDefaults(mpvs);
        this.checkFieldMarkers(mpvs);
        this.adaptEmptyArrayIndices(mpvs);
		// 主要是调用父类的绑定方法
        super.doBind(mpvs);
    }
	
	
	
	// 父类DataBinder里面的绑定方法
	    protected void doBind(MutablePropertyValues mpvs) {
        this.checkAllowedFields(mpvs);
        this.checkRequiredFields(mpvs);
		// 主要是这个方法
        this.applyPropertyValues(mpvs);
    }
	
	
	
		// 这个方法也是DataBinder里面的
	    protected void applyPropertyValues(MutablePropertyValues mpvs) {
        try {
		// 设置属性值
            this.getPropertyAccessor().setPropertyValues(mpvs, this.isIgnoreUnknownFields(), this.isIgnoreInvalidFields());
        } catch (PropertyBatchUpdateException var7) {
            PropertyAccessException[] var3 = var7.getPropertyAccessExceptions();
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                PropertyAccessException pae = var3[var5];
                this.getBindingErrorProcessor().processPropertyAccessException(pae, this.getInternalBindingResult());
            }
        }

    }
	
	
	
	
	
	// 具体设置属性值的逻辑,AbstractPropertyAccessor这个类里面的方法
	 public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException {
        List<PropertyAccessException> propertyAccessExceptions = null;
		// 获取所有需要设置进去的属性值,也就是请求形成的k-v对
        List<PropertyValue> propertyValues = pvs instanceof MutablePropertyValues ? ((MutablePropertyValues)pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues());
        if (ignoreUnknown) {
            this.suppressNotWritablePropertyException = true;
        }

        try {
		// 循环遍历数据
            Iterator var6 = propertyValues.iterator();

            while(var6.hasNext()) {
                PropertyValue pv = (PropertyValue)var6.next();

                try {
				// 设置属性值
                    this.setPropertyValue(pv);
                } catch (NotWritablePropertyException var14) {
                    if (!ignoreUnknown) {
                        throw var14;
                    }
                } catch (NullValueInNestedPathException var15) {
                    if (!ignoreInvalid) {
                        throw var15;
                    }
                } catch (PropertyAccessException var16) {
                    if (propertyAccessExceptions == null) {
                        propertyAccessExceptions = new ArrayList();
                    }

                    propertyAccessExceptions.add(var16);
                }
            }
        } finally {
            if (ignoreUnknown) {
                this.suppressNotWritablePropertyException = false;
            }

        }

        if (propertyAccessExceptions != null) {
            PropertyAccessException[] paeArray = (PropertyAccessException[])propertyAccessExceptions.toArray(new PropertyAccessException[0]);
            throw new PropertyBatchUpdateException(paeArray);
        }
    }




	// 这段源码就是上面设置属性值的具体代码,AbstractNestablePropertyAccessor类里面的方法
    public void setPropertyValue(PropertyValue pv) throws BeansException {
        PropertyTokenHolder tokens = (PropertyTokenHolder)pv.resolvedTokens;
        if (tokens == null) {
            String propertyName = pv.getName();

            AbstractNestablePropertyAccessor nestedPa;
            try {
                nestedPa = this.getPropertyAccessorForPropertyPath(propertyName);
            } catch (NotReadablePropertyException var6) {
                throw new NotWritablePropertyException(this.getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", var6);
            }

            tokens = this.getPropertyNameTokens(this.getFinalPath(nestedPa, propertyName));
            if (nestedPa == this) {
                pv.getOriginalPropertyValue().resolvedTokens = tokens;
            }
			// 这里是利用反射进行设置值的核心逻辑
            nestedPa.setPropertyValue(tokens, pv);
        } else {
            this.setPropertyValue(tokens, pv);
        }

    }




	// AbstractNestablePropertyAccessor类里面的方法
    protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
        if (tokens.keys != null) {
            this.processKeyedProperty(tokens, pv);
        } else {
            this.processLocalProperty(tokens, pv);
        }

    }
	
	
	
	
	// 设置属性值的具体逻辑,AbstractNestablePropertyAccessor类里面的方法
	  private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
        PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName);
        if (ph != null && ph.isWritable()) {
            Object oldValue = null;

            PropertyChangeEvent propertyChangeEvent;
            try {
				// 拿到请求中具体的值,例如年龄 12等等
                Object originalValue = pv.getValue();
                Object valueToApply = originalValue;
                if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
                    if (pv.isConverted()) {
                        valueToApply = pv.getConvertedValue();
                    } else {
                        if (this.isExtractOldValueForEditor() && ph.isReadable()) {
                            try {
                                oldValue = ph.getValue();
                            } catch (Exception var8) {
                                Exception ex = var8;
                                if (var8 instanceof PrivilegedActionException) {
                                    ex = ((PrivilegedActionException)var8).getException();
                                }

                                if (logger.isDebugEnabled()) {
                                    logger.debug("Could not read previous value of property '" + this.nestedPath + tokens.canonicalName + "'", ex);
                                }
                            }
                        }
						// 这里是最主要的,首先会进行请求参数的数据转换,例如将String类型的年龄转为Integer类型的年龄。。
                        valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
                    }

                    pv.getOriginalPropertyValue().conversionNecessary = valueToApply != originalValue;
                }

                ph.setValue(valueToApply);
            } catch (TypeMismatchException var9) {
                throw var9;
            } catch (InvocationTargetException var10) {
                propertyChangeEvent = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
                if (var10.getTargetException() instanceof ClassCastException) {
                    throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), var10.getTargetException());
                } else {
                    Throwable cause = var10.getTargetException();
                    if (cause instanceof UndeclaredThrowableException) {
                        cause = cause.getCause();
                    }

                    throw new MethodInvocationException(propertyChangeEvent, cause);
                }
            } catch (Exception var11) {
                propertyChangeEvent = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
                throw new MethodInvocationException(propertyChangeEvent, var11);
            }
        } else if (pv.isOptional()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignoring optional value for property '" + tokens.actualName + "' - property not found on bean class [" + this.getRootClass().getName() + "]");
            }

        } else if (!this.suppressNotWritablePropertyException) {
            throw this.createNotWritablePropertyException(tokens.canonicalName);
        }
    }
	
	
	
	
	
	// 数据转换器,AbstractNestablePropertyAccessor类里面的方法
	    @Nullable
    private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td) throws TypeMismatchException {
        Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");

        PropertyChangeEvent pce;
        try {
			// 这里使用了代理,进入查看数据是如何转化的
            return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
        } catch (IllegalStateException | ConverterNotFoundException var8) {
            pce = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + propertyName, oldValue, newValue);
            throw new ConversionNotSupportedException(pce, requiredType, var8);
        } catch (IllegalArgumentException | ConversionException var9) {
            pce = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + propertyName, oldValue, newValue);
            throw new TypeMismatchException(pce, requiredType, var9);
        }
    }
	
	
	
	
	
	
	// 数据转化具体逻辑,TypeConverterDelegate里面的方法
	    @Nullable
    public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
        PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
        ConversionFailedException conversionAttemptEx = null;
		// 这里是获取124个转换器服务
        ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
            TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			// 然后判断那个转换器能够进行转换
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                try {
					// 这里就开始进行数据转换了
                    return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                } catch (ConversionFailedException var14) {
                    conversionAttemptEx = var14;
                }
            }
        }

        Object convertedValue = newValue;
        if (editor != null || requiredType != null && !ClassUtils.isAssignableValue(requiredType, newValue)) {
            if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && newValue instanceof String) {
                TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
                if (elementTypeDesc != null) {
                    Class<?> elementType = elementTypeDesc.getType();
                    if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
                        convertedValue = StringUtils.commaDelimitedListToStringArray((String)newValue);
                    }
                }
            }

            if (editor == null) {
                editor = this.findDefaultEditor(requiredType);
            }

            convertedValue = this.doConvertValue(oldValue, convertedValue, requiredType, editor);
        }

        boolean standardConversion = false;
        if (requiredType != null) {
            if (convertedValue != null) {
                if (Object.class == requiredType) {
                    return convertedValue;
                }

                if (requiredType.isArray()) {
                    if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
                        convertedValue = StringUtils.commaDelimitedListToStringArray((String)convertedValue);
                    }

                    return this.convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
                }

                if (convertedValue instanceof Collection) {
                    convertedValue = this.convertToTypedCollection((Collection)convertedValue, propertyName, requiredType, typeDescriptor);
                    standardConversion = true;
                } else if (convertedValue instanceof Map) {
                    convertedValue = this.convertToTypedMap((Map)convertedValue, propertyName, requiredType, typeDescriptor);
                    standardConversion = true;
                }

                if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
                    convertedValue = Array.get(convertedValue, 0);
                    standardConversion = true;
                }

                if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
                    return convertedValue.toString();
                }

                if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
                    if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
                        try {
                            Constructor<T> strCtor = requiredType.getConstructor(String.class);
                            return BeanUtils.instantiateClass(strCtor, new Object[]{convertedValue});
                        } catch (NoSuchMethodException var12) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("No String constructor found on type [" + requiredType.getName() + "]", var12);
                            }
                        } catch (Exception var13) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", var13);
                            }
                        }
                    }

                    String trimmedValue = ((String)convertedValue).trim();
                    if (requiredType.isEnum() && trimmedValue.isEmpty()) {
                        return null;
                    }

                    convertedValue = this.attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
                    standardConversion = true;
                } else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
                    convertedValue = NumberUtils.convertNumberToTargetClass((Number)convertedValue, requiredType);
                    standardConversion = true;
                }
            } else if (requiredType == Optional.class) {
                convertedValue = Optional.empty();
            }

            if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
                if (conversionAttemptEx != null) {
                    throw conversionAttemptEx;
                }

                if (conversionService != null && typeDescriptor != null) {
                    TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
                    if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                        return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                    }
                }

                StringBuilder msg = new StringBuilder();
                msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
                msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
                if (propertyName != null) {
                    msg.append(" for property '").append(propertyName).append("'");
                }

                if (editor != null) {
                    msg.append(": PropertyEditor [").append(editor.getClass().getName()).append("] returned inappropriate value of type '").append(ClassUtils.getDescriptiveType(convertedValue)).append("'");
                    throw new IllegalArgumentException(msg.toString());
                }

                msg.append(": no matching editors or conversion strategy found");
                throw new IllegalStateException(msg.toString());
            }
        }

        if (conversionAttemptEx != null) {
            if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
                throw conversionAttemptEx;
            }

            logger.debug("Original ConversionService attempt failed - ignored since PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
        }

        return convertedValue;
    }
	
	
	
	
	
	
		// 判断能够进行转换
		public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        Assert.notNull(targetType, "Target type to convert to cannot be null");
        if (sourceType == null) {
            return true;
        } else {
            GenericConverter converter = this.getConverter(sourceType, targetType);
            return converter != null;
        }
    }




	// GenericConversionService类里面的方法,具体判断逻辑
    @Nullable
    protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
        ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
		// 先从缓存中获取
        GenericConverter converter = (GenericConverter)this.converterCache.get(key);
        if (converter != null) {
            return converter != NO_MATCH ? converter : null;
        } else {
		
		// 缓存中没有,然后进行判断逻辑
            converter = this.converters.find(sourceType, targetType);
            // 如果没有找到就给一个默认的
			if (converter == null) {
                converter = this.getDefaultConverter(sourceType, targetType);
            }
			// 放入缓存
            if (converter != null) {
                this.converterCache.put(key, converter);
                return converter;
            } else {
                this.converterCache.put(key, NO_MATCH);
                return null;
            }
        }
    }
	
	
	
	// GenericConversionService类里面的方法,这里就是具体如何找的,其实就是循环遍历,那个能够将sourceType请求带来的参数类型--》转换成targetType目标类型
	 @Nullable
        public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
            List<Class<?>> sourceCandidates = this.getClassHierarchy(sourceType.getType());
            List<Class<?>> targetCandidates = this.getClassHierarchy(targetType.getType());
            Iterator var5 = sourceCandidates.iterator();
			// 两次循环遍历
            while(var5.hasNext()) {
                Class<?> sourceCandidate = (Class)var5.next();
                Iterator var7 = targetCandidates.iterator();

                while(var7.hasNext()) {
                    Class<?> targetCandidate = (Class)var7.next();
                    GenericConverter.ConvertiblePair convertiblePair = new GenericConverter.ConvertiblePair(sourceCandidate, targetCandidate);
                    GenericConverter converter = this.getRegisteredConverter(sourceType, targetType, convertiblePair);
                    if (converter != null) {
                        return converter;
                    }
                }
            }

            return null;
        }




		// GenericConversionService类里面的方法,
        @Nullable
        private GenericConverter getRegisteredConverter(TypeDescriptor sourceType, TypeDescriptor targetType, GenericConverter.ConvertiblePair convertiblePair) {
			// this.converters是一个map,通过convertiblePair为key(“java.lang.String -> java.lang.Integer”)获取转换器
			ConvertersForPair convertersForPair = (ConvertersForPair)this.converters.get(convertiblePair);
            if (convertersForPair != null) {
				// 这里其实就是获取了转换器,然后返回
                GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
                if (converter != null) {
                    return converter;
                }
            }

            Iterator var7 = this.globalConverters.iterator();

            GenericConverter globalConverter;
            do {
                if (!var7.hasNext()) {
                    return null;
                }

                globalConverter = (GenericConverter)var7.next();
            } while(!((ConditionalConverter)globalConverter).matches(sourceType, targetType));

            return globalConverter;
        }


这里有124个转换器,会将http超文本协议传输过来的参数进行转换。

 二、准备进行数据转换与绑定数据

// 开始进行参数解析
    @Nullable
    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
        Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
        String name = ModelFactory.getNameForParameter(parameter);
        ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
        if (ann != null) {
            mavContainer.setBinding(name, ann.binding());
        }

        Object attribute = null;
        BindingResult bindingResult = null;
        if (mavContainer.containsAttribute(name)) {
            attribute = mavContainer.getModel().get(name);
        } else {
		// 这里以上都不用管,以上是使用ModelAttribute注解处理的逻辑
            try {
				// 创建一个空的参数对象,也就是接口参数里面的自定义javaBean对象,但是里面的属性值是空的。之后就会让这个对象的属性值与请求进来所带的参数进行一一绑定。
                attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
            } catch (BindException var10) {
                if (this.isBindExceptionRequired(parameter)) {
                    throw var10;
                }

                if (parameter.getParameterType() == Optional.class) {
                    attribute = Optional.empty();
                } else {
                    attribute = var10.getTarget();
                }

                bindingResult = var10.getBindingResult();
            }
        }

		// 如果没有请求进来的参数进行绑定,就会进入下面进行绑定
        if (bindingResult == null) {
			// 创建绑定器,这里会把原生请求对象以及空属性person对象封装进去。同时还有各种类型的转换器,
			// 例如,所有请求都是通过http超文本协议传过来的,所以所有请求参数都是文本类型也就是String,比如年龄12,请求进来就会把"12"字符串类型转为Integer类型
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) {
                if (!mavContainer.isBindingDisabled(name)) {
				// 这里就进行了参数绑定,也就是给Person绑定上了请求所携带的值。主要逻辑在里面
                    this.bindRequestParameters(binder, webRequest);
                }

                this.validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
                    throw new BindException(binder.getBindingResult());
                }
            }

            if (!parameter.getParameterType().isInstance(attribute)) {
                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
            }

            bindingResult = binder.getBindingResult();
        }

        Map<String, Object> bindingResultModel = bindingResult.getModel();
        mavContainer.removeAttributes(bindingResultModel);
        mavContainer.addAllAttributes(bindingResultModel);
        return attribute;
    }
	
	
	
		// 这里会调用父类的方法创建一个Perrson对象
	    protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
        String value = this.getRequestValueForAttribute(attributeName, request);
        if (value != null) {
            Object attribute = this.createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
            if (attribute != null) {
                return attribute;
            }
        }
		// 会进到这里
        return super.createAttribute(attributeName, parameter, binderFactory, request);
    }
	
	
	
	
		// 具体创建一个person对象逻辑
	    protected Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
	   // 通过MethodParameter对象获取接口参数
	   MethodParameter nestedParameter = parameter.nestedIfOptional();
	   // 获取接口参数类型
        Class<?> clazz = nestedParameter.getNestedParameterType();
		// 获取参数类型对应的构造函数
        Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
        // 这里就是通过底层反射之类的构造Perrson对象
		Object attribute = this.constructAttribute(ctor, attributeName, parameter, binderFactory, webRequest);
        if (parameter != nestedParameter) {
            attribute = Optional.of(attribute);
        }

        return attribute;
    }
	
	
	
	
	
	
		// 这里就进行了参数绑定,ServletModelAttributeMethodProcessor中的bindRequestParameters方法
	    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
		// 获取请求
        ServletRequest servletRequest = (ServletRequest)request.getNativeRequest(ServletRequest.class);
        Assert.state(servletRequest != null, "No ServletRequest");
        ServletRequestDataBinder servletBinder = (ServletRequestDataBinder)binder;
        // 这里进行了数据绑定
		servletBinder.bind(servletRequest);
    }
	
	
	
		// 对应上面的bind数据绑定方法,ServletRequestDataBinder类里面的
	    public void bind(ServletRequest request) {
		// 获取请求中参数的k-v数据对
        MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
        MultipartRequest multipartRequest = (MultipartRequest)WebUtils.getNativeRequest(request, MultipartRequest.class);
        if (multipartRequest != null) {
            this.bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
        } else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/")) {
            HttpServletRequest httpServletRequest = (HttpServletRequest)WebUtils.getNativeRequest(request, HttpServletRequest.class);
            if (httpServletRequest != null) {
                StandardServletPartUtils.bindParts(httpServletRequest, mpvs, this.isBindEmptyMultipartFiles());
            }
        }

        this.addBindValues(mpvs, request);
		// 这里是真正进行参数绑定的方法
        this.doBind(mpvs);
    }
	
	
	
	// WebDataBinder类里面的绑定方法
    protected void doBind(MutablePropertyValues mpvs) {
        this.checkFieldDefaults(mpvs);
        this.checkFieldMarkers(mpvs);
        this.adaptEmptyArrayIndices(mpvs);
		// 主要是调用父类的绑定方法
        super.doBind(mpvs);
    }
	
	
	
	// 父类DataBinder里面的绑定方法
	    protected void doBind(MutablePropertyValues mpvs) {
        this.checkAllowedFields(mpvs);
        this.checkRequiredFields(mpvs);
		// 主要是这个方法
        this.applyPropertyValues(mpvs);
    }
	
	
	
		// 这个方法也是DataBinder里面的
	    protected void applyPropertyValues(MutablePropertyValues mpvs) {
        try {
		// 设置属性值
            this.getPropertyAccessor().setPropertyValues(mpvs, this.isIgnoreUnknownFields(), this.isIgnoreInvalidFields());
        } catch (PropertyBatchUpdateException var7) {
            PropertyAccessException[] var3 = var7.getPropertyAccessExceptions();
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                PropertyAccessException pae = var3[var5];
                this.getBindingErrorProcessor().processPropertyAccessException(pae, this.getInternalBindingResult());
            }
        }

    }
	
	
	
	
	
	// 具体设置属性值的逻辑,AbstractPropertyAccessor这个类里面的方法
	 public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) throws BeansException {
        List<PropertyAccessException> propertyAccessExceptions = null;
		// 获取所有需要设置进去的属性值,也就是请求形成的k-v对
        List<PropertyValue> propertyValues = pvs instanceof MutablePropertyValues ? ((MutablePropertyValues)pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues());
        if (ignoreUnknown) {
            this.suppressNotWritablePropertyException = true;
        }

        try {
		// 循环遍历数据
            Iterator var6 = propertyValues.iterator();

            while(var6.hasNext()) {
                PropertyValue pv = (PropertyValue)var6.next();

                try {
				// 设置属性值
                    this.setPropertyValue(pv);
                } catch (NotWritablePropertyException var14) {
                    if (!ignoreUnknown) {
                        throw var14;
                    }
                } catch (NullValueInNestedPathException var15) {
                    if (!ignoreInvalid) {
                        throw var15;
                    }
                } catch (PropertyAccessException var16) {
                    if (propertyAccessExceptions == null) {
                        propertyAccessExceptions = new ArrayList();
                    }

                    propertyAccessExceptions.add(var16);
                }
            }
        } finally {
            if (ignoreUnknown) {
                this.suppressNotWritablePropertyException = false;
            }

        }

        if (propertyAccessExceptions != null) {
            PropertyAccessException[] paeArray = (PropertyAccessException[])propertyAccessExceptions.toArray(new PropertyAccessException[0]);
            throw new PropertyBatchUpdateException(paeArray);
        }
    }




	// 这段源码就是上面设置属性值的具体代码,AbstractNestablePropertyAccessor类里面的方法
    public void setPropertyValue(PropertyValue pv) throws BeansException {
        PropertyTokenHolder tokens = (PropertyTokenHolder)pv.resolvedTokens;
        if (tokens == null) {
            String propertyName = pv.getName();

            AbstractNestablePropertyAccessor nestedPa;
            try {
                nestedPa = this.getPropertyAccessorForPropertyPath(propertyName);
            } catch (NotReadablePropertyException var6) {
                throw new NotWritablePropertyException(this.getRootClass(), this.nestedPath + propertyName, "Nested property in path '" + propertyName + "' does not exist", var6);
            }

            tokens = this.getPropertyNameTokens(this.getFinalPath(nestedPa, propertyName));
            if (nestedPa == this) {
                pv.getOriginalPropertyValue().resolvedTokens = tokens;
            }
			// 这里是利用反射进行设置值的核心逻辑
            nestedPa.setPropertyValue(tokens, pv);
        } else {
            this.setPropertyValue(tokens, pv);
        }

    }




	// AbstractNestablePropertyAccessor类里面的方法
    protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
        if (tokens.keys != null) {
            this.processKeyedProperty(tokens, pv);
        } else {
            this.processLocalProperty(tokens, pv);
        }

    }
	
	
	
	
	// 设置属性值的具体逻辑,AbstractNestablePropertyAccessor类里面的方法
	  private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
        PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName);
        if (ph != null && ph.isWritable()) {
            Object oldValue = null;

            PropertyChangeEvent propertyChangeEvent;
            try {
				// 拿到请求中具体的值,例如年龄 12等等
                Object originalValue = pv.getValue();
                Object valueToApply = originalValue;
                if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
                    if (pv.isConverted()) {
                        valueToApply = pv.getConvertedValue();
                    } else {
                        if (this.isExtractOldValueForEditor() && ph.isReadable()) {
                            try {
                                oldValue = ph.getValue();
                            } catch (Exception var8) {
                                Exception ex = var8;
                                if (var8 instanceof PrivilegedActionException) {
                                    ex = ((PrivilegedActionException)var8).getException();
                                }

                                if (logger.isDebugEnabled()) {
                                    logger.debug("Could not read previous value of property '" + this.nestedPath + tokens.canonicalName + "'", ex);
                                }
                            }
                        }
						// 这里是最主要的,首先会进行请求参数的数据转换,例如将String类型的年龄转为Integer类型的年龄。。
                        valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
                    }
					// 到这里就完成了类型转换,
                    pv.getOriginalPropertyValue().conversionNecessary = valueToApply != originalValue;
                }
				// 这里就将转换好的数据设置进value中,其他属性也是这样的步骤,这里是一个循环遍历设置的逻辑,循环代码在上面,这里的value就是12,name就是age,也就是在这里进行了数据绑定
                ph.setValue(valueToApply);
            } catch (TypeMismatchException var9) {
                throw var9;
            } catch (InvocationTargetException var10) {
                propertyChangeEvent = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
                if (var10.getTargetException() instanceof ClassCastException) {
                    throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), var10.getTargetException());
                } else {
                    Throwable cause = var10.getTargetException();
                    if (cause instanceof UndeclaredThrowableException) {
                        cause = cause.getCause();
                    }

                    throw new MethodInvocationException(propertyChangeEvent, cause);
                }
            } catch (Exception var11) {
                propertyChangeEvent = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
                throw new MethodInvocationException(propertyChangeEvent, var11);
            }
        } else if (pv.isOptional()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Ignoring optional value for property '" + tokens.actualName + "' - property not found on bean class [" + this.getRootClass().getName() + "]");
            }

        } else if (!this.suppressNotWritablePropertyException) {
            throw this.createNotWritablePropertyException(tokens.canonicalName);
        }
    }
	
	
	
	
	
	// 数据转换器,AbstractNestablePropertyAccessor类里面的方法
	    @Nullable
    private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<?> requiredType, @Nullable TypeDescriptor td) throws TypeMismatchException {
        Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");

        PropertyChangeEvent pce;
        try {
			// 这里使用了代理,进入查看数据是如何转化的
            return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
        } catch (IllegalStateException | ConverterNotFoundException var8) {
            pce = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + propertyName, oldValue, newValue);
            throw new ConversionNotSupportedException(pce, requiredType, var8);
        } catch (IllegalArgumentException | ConversionException var9) {
            pce = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + propertyName, oldValue, newValue);
            throw new TypeMismatchException(pce, requiredType, var9);
        }
    }
	
	
	
	
	
	
	// 数据转化具体逻辑,TypeConverterDelegate里面的方法
	    @Nullable
    public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
        PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
        ConversionFailedException conversionAttemptEx = null;
		// 这里是获取124个转换器服务
        ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
            TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			// 然后判断那个转换器能够进行转换
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                try {
					// 这里就开始进行数据转换了
                    return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                } catch (ConversionFailedException var14) {
                    conversionAttemptEx = var14;
                }
            }
        }

        Object convertedValue = newValue;
        if (editor != null || requiredType != null && !ClassUtils.isAssignableValue(requiredType, newValue)) {
            if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && newValue instanceof String) {
                TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
                if (elementTypeDesc != null) {
                    Class<?> elementType = elementTypeDesc.getType();
                    if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
                        convertedValue = StringUtils.commaDelimitedListToStringArray((String)newValue);
                    }
                }
            }

            if (editor == null) {
                editor = this.findDefaultEditor(requiredType);
            }

            convertedValue = this.doConvertValue(oldValue, convertedValue, requiredType, editor);
        }

        boolean standardConversion = false;
        if (requiredType != null) {
            if (convertedValue != null) {
                if (Object.class == requiredType) {
                    return convertedValue;
                }

                if (requiredType.isArray()) {
                    if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
                        convertedValue = StringUtils.commaDelimitedListToStringArray((String)convertedValue);
                    }

                    return this.convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
                }

                if (convertedValue instanceof Collection) {
                    convertedValue = this.convertToTypedCollection((Collection)convertedValue, propertyName, requiredType, typeDescriptor);
                    standardConversion = true;
                } else if (convertedValue instanceof Map) {
                    convertedValue = this.convertToTypedMap((Map)convertedValue, propertyName, requiredType, typeDescriptor);
                    standardConversion = true;
                }

                if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
                    convertedValue = Array.get(convertedValue, 0);
                    standardConversion = true;
                }

                if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
                    return convertedValue.toString();
                }

                if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
                    if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
                        try {
                            Constructor<T> strCtor = requiredType.getConstructor(String.class);
                            return BeanUtils.instantiateClass(strCtor, new Object[]{convertedValue});
                        } catch (NoSuchMethodException var12) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("No String constructor found on type [" + requiredType.getName() + "]", var12);
                            }
                        } catch (Exception var13) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", var13);
                            }
                        }
                    }

                    String trimmedValue = ((String)convertedValue).trim();
                    if (requiredType.isEnum() && trimmedValue.isEmpty()) {
                        return null;
                    }

                    convertedValue = this.attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
                    standardConversion = true;
                } else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
                    convertedValue = NumberUtils.convertNumberToTargetClass((Number)convertedValue, requiredType);
                    standardConversion = true;
                }
            } else if (requiredType == Optional.class) {
                convertedValue = Optional.empty();
            }

            if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
                if (conversionAttemptEx != null) {
                    throw conversionAttemptEx;
                }

                if (conversionService != null && typeDescriptor != null) {
                    TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
                    if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                        return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                    }
                }

                StringBuilder msg = new StringBuilder();
                msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
                msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
                if (propertyName != null) {
                    msg.append(" for property '").append(propertyName).append("'");
                }

                if (editor != null) {
                    msg.append(": PropertyEditor [").append(editor.getClass().getName()).append("] returned inappropriate value of type '").append(ClassUtils.getDescriptiveType(convertedValue)).append("'");
                    throw new IllegalArgumentException(msg.toString());
                }

                msg.append(": no matching editors or conversion strategy found");
                throw new IllegalStateException(msg.toString());
            }
        }

        if (conversionAttemptEx != null) {
            if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
                throw conversionAttemptEx;
            }

            logger.debug("Original ConversionService attempt failed - ignored since PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
        }

        return convertedValue;
    }
	
	
	
	// 开始进行数据转换,GenericConversionService类里面的方法
	// sourceType:源数据类型
	// targetType:目标数据类型
	// source:源数据
	    @Nullable
    public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
        Assert.notNull(targetType, "Target type to convert to cannot be null");
        if (sourceType == null) {
            Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
            return this.handleResult((TypeDescriptor)null, targetType, this.convertNullSource((TypeDescriptor)null, targetType));
        } else 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() + "]");
        } else {
            GenericConverter converter = this.getConverter(sourceType, targetType);
            if (converter != null) {
                Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
                return this.handleResult(sourceType, targetType, result);
            } else {
                return this.handleConverterNotFound(source, sourceType, targetType);
            }
        }
    }
	
	
	
	
		
		// 调用converter里面的convert方法进行数据转换,ConversionUtils这个类里面的
	
	    @Nullable
    public static Object invokeConverter(GenericConverter converter, @Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        try {
            return converter.convert(source, sourceType, targetType);
        } catch (ConversionFailedException var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new ConversionFailedException(sourceType, targetType, source, var6);
        }
    }



	// 会调用StringToNumberConverterFactory类下的这个方法进行转换
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumber(targetType);
    }	
	
	
	
	
	// 同一个类下的私有方法,StringToNumber对象,将String转为Number数据
	// 未来我们可以给WebDataBinder里面放自己的Converter;也就是说可以模仿这个类进行自定义转换,然后放入WebDataBinder绑定器中
	private static final class StringToNumber<T extends Number> implements Converter<String, T> {
        private final Class<T> targetType;

        public StringToNumber(Class<T> targetType) {
            this.targetType = targetType;
        }

        @Nullable
        public T convert(String source) {
			// 这里就是真正的转换
            return source.isEmpty() ? null : 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 (Byte.class == targetClass) {
            return isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed);
        } else if (Short.class == targetClass) {
            return isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed);
        } else if (Integer.class == targetClass) {
			// 例如年龄是Integer,会进入到这里,将String 类型转为Ingeter类型,将String类型进行解码Integer.decode(trimmed)
            return isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed);
        } else if (Long.class == targetClass) {
            return isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed);
        } else if (BigInteger.class == targetClass) {
            return isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed);
        } else if (Float.class == targetClass) {
            return Float.valueOf(trimmed);
        } else if (Double.class == targetClass) {
            return Double.valueOf(trimmed);
        } else if (BigDecimal.class != targetClass && Number.class != targetClass) {
            throw new IllegalArgumentException("Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]");
        } else {
            return new BigDecimal(trimmed);
        }
    }

三、自定义类型转换器

测试用例中,访问接口进行传参,接口参数中的Pet对象,里面的属性是这样传递的。pet.name=xxx以及pet.age = 12.如果我们缓存pet = 小猫,12;其中小猫是name的值,12是age的值,这样springmvc就没法进行类型绑定,这是由于WebDataBinder 中的Converters没办法进行将请求参数与javabean进行类型绑定。所以需要我们自定义类型转换器。进行数据类型转换,然后绑定。

 

 一、自定义类型转换器

// 类型转换函数表达式,S表示请求进来的参数,T表示转换成的目标参数
@FunctionalInterface
public interface Converter<S, T> {
    @Nullable
    T convert(S var1);

    default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
        Assert.notNull(after, "After Converter must not be null");
        return (s) -> {
            T initialResult = this.convert(s);
            return initialResult != null ? after.convert(initialResult) : null;
        };
    }
}

需要在spring容器中注入自定义的类型转换器。

@Configuration
public class SpringMvcConfig {


    /**WebMvcConfigurer可以定制化springmvc功能
     * 这个接口里面有一个方法,添加类型转化器以及格式化器(比如说日期格式化)
     *     default void addFormatters(FormatterRegistry registry) {
     *     }
     *   这里添加请求参数转换Person中Pet属性的转换器,只要是String-》Person.Pet对象的都是走这个转换器
     * @return
     */
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {

                registry.addConverter(new Converter<String, Person.Pet>() {
                    @Override
                    public Person.Pet convert(String source) {
                         if (!StringUtils.hasLength(source)){
                            return null;
                        }
                        
                        String[] split = source.split(",");
                        Person.Pet pet = new Person.Pet();
                        pet.setName(split[0]);
                        pet.setAge(split[1]);
                        return pet;
                    }
                });
            }
        };
    }



}

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有效三⻆形的个数 05

有效三⻆形的个数 我写的 class Solution {public int triangleNumber(int[] nums) {Arrays.sort(nums);int sum0;for (int knums.length-1;k>1;k--) {int left0;int rightk-1;while(right>left) {if (nums[left]nums[right]>nums[k]) {sum(right-left);right--;…

QD1-P26、27、28 CSS 属性 文本

本节&#xff08;P26、27、28 三合一&#xff09;学习&#xff1a;CSS 文本属性。 ‍ 本节视频 https://www.bilibili.com/video/BV1n64y1U7oj?p26 CSS&#xff08;层叠样式表&#xff09;中用于设置文本样式的属性有很多&#xff0c;以下是一些常用的文本属性&#xff1a; …

[数据结构]栈的实现与应用

文章目录 一、引言二、栈的基本概念1、栈是什么2、栈的实现方式对比3、函数栈帧 三、栈的实现1、结构体定义2、初始化3、销毁4、显示5、数据操作 四、分析栈1、优点2、缺点 五、总结1、练习题2、源代码 一、引言 栈&#xff0c;作为一种基础且重要的数据结构&#xff0c;在计算…

Mybatis高级查询-一对多查询

表介绍和表关系说明 新建以下4张表 tb_user&#xff1a;用户表 tb_order&#xff1a;订单表 tb_item&#xff1a;商品表 tb_orderdetail&#xff1a;订单详情表 【表关系】 1.tb_user和 tb_order表关系tb_user 《》 tb_order&#xff1a;一对多&#xff0c; 一个人可以下多…

Oracle漏洞修复 19.3 补丁包 升级为19.22

1.场景描述 上周末2024-10-12日,服务器扫出漏洞,希望及时修复。其中,oracle的漏洞清单如下,总结了下,基本都是 Oracle Database Server 的 19.3 版本到 19.20 版本和 21.3 版本到 21.11 版本存在安全漏洞,即版本问题。如: Oracle Database Server 安全漏洞(CVE-2023-22…

241013深度学习之GoogleLeNet

文章目录 1. GoogleLeNet2.Inception块3.googleLeNet模型4.训练模型4.1运行结果图 5.小结 1. GoogleLeNet 全称&#xff1a;含并行连接的网络 在2014年的ImageNet图像识别挑战赛中&#xff0c;一个名叫GoogLeNet (Szegedy et al., 2015)的网络架构大放异彩。 GoogLeNet吸收了N…

ZED相机的使用

ZED双目相机型号是ZED 2 立体相机&#xff08;ZED2 Stereo Camera&#xff0c;Stereolabs , San Francisco, USA&#xff09;&#xff0c;其中还包括USB 3.0 数据线&#xff0c;以及一个迷你三脚架。 一、查看自己的cuda nvidia-smi # 查看显卡驱动信息 nvcc -V # 查看CUDA的…

C++——vector的了解与使用

目录 引言 vector容器的基本概念 1.功能 2.动态大小 3.动态扩展 vector的接口 1.vector的迭代器 2.vector的初始化与销毁 3.vector的容量操作 3.1 有效长度和容量大小 (1)使用示例 (2)扩容机制 3.2 有效长度和容量操作 (1)reserve (2)resize 4.vector的访问操作…

电脑基础知识:mfc110.dll丢失的解决方法

1.mfc110.dll 丢失常见原因 mfc110.dll 文件的丢失或损坏是Windows系统中常见的问题&#xff0c;它可能由多种原因引起&#xff0c;以下是一些主要的因素&#xff1a; 不完全的软件卸载 在卸载程序时&#xff0c;如果相关的 DLL 文件没有被正确移除&#xff0c;可能会导致文件…

aarch64-linux-gnu-gcc交叉编译opencv移植到linuxaarch64上

所谓的将OpenCV移植到某个平台&#xff0c;就是用该平台对应的编译器将OpenCV源代码编译成so库 1.下载3.4.5opencv源码 解压 安装aarch64-linux-gnu-gcc 在执行 bash build-linux_RK3588.sh过程中&#xff0c;提示什么&#xff0c;装就行了。 装完之后 trolongtrolong-virtu…

机器学习的四大学派:符号主义学派、贝叶斯学派、连接主义学派与进化仿生学派

目录 前言1. 符号主义学派1.1 含义与理论基础1.2 特点1.3 应用 2. 贝叶斯学派2.1 含义与理论基础2.2 特点2.3 应用 3. 连接主义学派3.1 含义与理论基础3.2 特点3.3 应用 4. 进化仿生学派4.1 含义与理论基础4.2 特点4.3 应用 结语 前言 机器学习作为人工智能的核心技术之一&…

【AUTOSAR 基础软件】ComM模块详解(通信管理)

文章包含了AUTOSAR基础软件&#xff08;BSW&#xff09;中ComM模块相关的内容详解。本文从AUTOSAR规范解析&#xff0c;ISOLAR-AB配置以及模块相关代码分析三个维度来帮读者清晰的认识和了解ComM这一基础软件模块。文中涉及的ISOLAR-AB配置以及模块相关代码都是依托于ETAS提供的…

2.随机变量及其分布

第二章 随机变量及其分布 1. 随机变量及其分布 1.1 随机变量的定义 定义1.1 随机变量 ​ 定义在样本空间 Ω \Omega Ω上的实值函数 X X ( ω ) XX(\omega) XX(ω)称为随机变量,常用大写字母 X , Y , Z X,Y,Z X,Y,Z等表示随机变量&#xff0c;其取值用小写字母 x , y , z …

手写mybatis之返回Insert操作自增索引值

前言 技术的把控&#xff0c;往往都是体现在细节上&#xff01; 如果说能用行&#xff0c;复制粘贴就能完成需求&#xff0c;出错了就手忙脚乱。那你一定不是一个高级开发&#xff0c;对很多的技术细节也都不了解。 目标 在前面所有的章节内容对 ORM 框架的实现中&#xff0c;其…

VMware16虚拟机安装macOS Monterey 12详细教程

1、虚拟机配置安装 安装WMware Workstation 16,打开安包装包,只需点下一步即可,安装过程略。 安装完毕后,检查任务管理器,如果有VMware程序运行,就结束任务。 打开【运行】,快捷键win+R,输入services.msc 找到所有VMware开头的服务 将这些VMware服务逐一停用。 鼠标…

机器学习(10.7-10.13)(Pytorch LSTM和LSTMP的原理及其手写复现)

文章目录 摘要Abstract1 LSTM1.1 使用Pytorch LSTM1.1.1 LSTM API代码实现1.1.2 LSTMP代码实现 1.2 手写一个lstm_forward函数 实现单向LSTM的计算原理1.3 手写一个lstmp_forward函数 实现单向LSTMP的计算原理总结 摘要 LSTM是RNN的一个优秀的变种模型&#xff0c;继承了大部分…

【论文阅读笔记】Bigtable: A Distributed Storage System for Structured Data

文章目录 1 简介2 数据模型2.1 行2.2 列族2.3 时间戳 3 API4 基础构建4.1 GFS4.2 SSTable4.3 Chubby 5 实现5.1 Tablet 位置5.2 Tablet 分配5.3 为 tablet 提供服务5.4 压缩5.4.1 小压缩5.4.2 主压缩 6 优化6.1 局部性组6.2 压缩6.3 缓存6.4 布隆过滤器6.5 Commit日志实现6.6 T…

金融信用评分卡建模项目1:工具雏形

最近我一直忙着开发一个信用评分卡建模工具&#xff0c;所以没有时间更新示例或动态。今天&#xff0c;我很高兴地跟大家分享&#xff0c;这个工具的基本框架已经完成了&#xff0c;并且探索性的将大语言模型&#xff08;AI&#xff09;整合了进去。目前ai在工具中扮演智能助手…

力扣面试150 从中序与后序遍历序列构造二叉树 递归

Problem: 106. 从中序与后序遍历序列构造二叉树 &#x1f468;‍&#x1f3eb; 参考题解 &#x1f37b; Code 1 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNo…

前端的AI工具:ChatGPT Canvas与Claude Artifacts对比 -仅仅是OpenAI一个迟来的追赶吗?- 贺星舰五飞试验成功

如果你对OpenAI的ChatGPT Canvas和Anthropic的Claude Artifacts有所耳闻&#xff0c;可能会想知道这两个工具有何不同&#xff0c;以及哪个能让你的工作流程更加顺畅。这两个工具旨在提升生产力&#xff0c;但侧重点各异——编码、写作、创意和实时反馈。 本文将深入探讨ChatG…