您的位置:首页 > 教育 > 培训 > 凡科自助建站平台_凯里网站建设公司哪家好_图片外链工具_怎么从网上找客户

凡科自助建站平台_凯里网站建设公司哪家好_图片外链工具_怎么从网上找客户

2024/12/21 20:15:46 来源:https://blog.csdn.net/weixin_42972832/article/details/142729032  浏览:    关键词:凡科自助建站平台_凯里网站建设公司哪家好_图片外链工具_怎么从网上找客户
凡科自助建站平台_凯里网站建设公司哪家好_图片外链工具_怎么从网上找客户

一、前言

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

二、原理

一、测试用例 

@Data
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;@Datapublic 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);}

二、参数解析

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

// 开始进行参数解析@Nullablepublic 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类里面的方法@Nullableprivate 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里面的方法@Nullablepublic <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类里面的方法,具体判断逻辑@Nullableprotected 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目标类型@Nullablepublic 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类里面的方法,@Nullableprivate 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超文本协议传输过来的参数进行转换。

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

// 开始进行参数解析@Nullablepublic 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类里面的方法@Nullableprivate 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里面的方法@Nullablepublic <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:源数据@Nullablepublic 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这个类里面的@Nullablepublic 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;}@Nullablepublic 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> {@NullableT 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*/@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Person.Pet>() {@Overridepublic 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;}});}};}}

 

 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com