注意不能再参数上加上@ResponseBody,底层直接通过jackson序列化,不会走自定义的转换器,而是通过序列化来转换字符串成为对象
转换器1的用法:
string->date类型
@InitBinder public void initBinder(WebDataBinder binder){ binder.registerCustomEditor(Date.class,new DateEditor()); }
继承PropertyEditorSupport重写setAsText该方法即可
public class DateEditor extends PropertyEditorSupport
核心源码分析:
用@ModelAttribute来修饰对象,所以走ModelAttributeMethodProcessor的参数解析
org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#createAttribute
org.springframework.web.method.annotation.ModelAttributeMethodProcessor#createAttribute
注意这里的方法:一定要有带参数的构造方法,否则直接结束了
org.springframework.web.method.annotation.ModelAttributeMethodProcessor#constructAttribute(java.lang.reflect.Constructor<?>, java.lang.String, org.springframework.core.MethodParameter, org.springframework.web.bind.support.WebDataBinderFactory, org.springframework.web.context.request.NativeWebRequest)
org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder
org.springframework.web.method.annotation.InitBinderDataBinderFactory#initBinder
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
在这里注意:进入controller之前会进入我们之前定义的initBinder的方法进行初始化转换器
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
初始化号转换器进行参数解析
for (int i = 0; i < paramNames.length; i++) { String paramName = paramNames[i]; Class<?> paramType = paramTypes[i]; Object value = webRequest.getParameterValues(paramName); if (value == null) { if (fieldDefaultPrefix != null) { value = webRequest.getParameter(fieldDefaultPrefix + paramName); } if (value == null && fieldMarkerPrefix != null) { if (webRequest.getParameter(fieldMarkerPrefix + paramName) != null) { value = binder.getEmptyValue(paramType); } } } try { MethodParameter methodParam = new FieldAwareConstructorParameter(ctor, i, paramName); if (value == null && methodParam.isOptional()) { args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null); } else { //在这里进行参数转换 args[i] = binder.convertIfNecessary(value, paramType, methodParam); } }
org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)
最关键的转换逻辑
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException { // Custom editor for this type? //根据我们定义的转换器来获取自定义的处理逻辑 PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName); ConversionFailedException conversionAttemptEx = null; // No custom editor but custom ConversionService specified? ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); //如果没有自定义的转换器,则使用系统默认的4中类型转换器 if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { try { //使用系统默认的转换器 return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); } catch (ConversionFailedException ex) { // fallback to default conversion logic below conversionAttemptEx = ex; } } } Object convertedValue = newValue; // Value not of required type? //使用自定义的editor来进行类型转换 if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue 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) convertedValue); } } } if (editor == null) { editor = findDefaultEditor(requiredType); } convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); } boolean standardConversion = false; //返回参数进行类型转换 if (requiredType != null) { // Try to apply some standard type conversion rules if appropriate. if (convertedValue != null) { if (Object.class == requiredType) { return (T) convertedValue; } else if (requiredType.isArray()) { // Array required -> apply appropriate conversion of elements. if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); } return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType()); } else if (convertedValue instanceof Collection) { // Convert elements to target type, if determined. convertedValue = convertToTypedCollection( (Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor); standardConversion = true; } else if (convertedValue instanceof Map) { // Convert keys and values to respective target type, if determined. convertedValue = 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())) { // We can stringify any primitive value... return (T) convertedValue.toString(); } else 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, convertedValue); } catch (NoSuchMethodException ex) { // proceed with field lookup if (logger.isTraceEnabled()) { logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex); } } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex); } } } String trimmedValue = ((String) convertedValue).trim(); if (requiredType.isEnum() && trimmedValue.isEmpty()) { // It's an empty enum identifier: reset the enum value to null. return null; } convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue); standardConversion = true; } else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) { convertedValue = NumberUtils.convertNumberToTargetClass( (Number) convertedValue, (Class<Number>) requiredType); standardConversion = true; } } else { // convertedValue == null if (requiredType == Optional.class) { convertedValue = Optional.empty(); } } if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) { if (conversionAttemptEx != null) { // Original exception from former ConversionService call above... throw conversionAttemptEx; } else if (conversionService != null && typeDescriptor != null) { // ConversionService not tried before, probably custom editor found // but editor couldn't produce the required type... TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); } } // Definitely doesn't match: throw IllegalArgumentException/IllegalStateException 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()); } else { 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 (T) convertedValue; }
return BeanUtils.instantiateClass(ctor, args);
就这样返回参数类型转换好了,通过反射方法进行对象赋值完成对象的转换,然后进入controller,controller层获取到了转换后的对象