springmvc的參數(shù)綁定+接收前端日期字符串

找接收前端日期字符串的方法,在最后,中間可略過(guò)。

參數(shù)轉(zhuǎn)換器:現(xiàn)在的springmvc使用的是Converter轉(zhuǎn)換器,它可以進(jìn)行大部分類(lèi)型的參數(shù)轉(zhuǎn)換,但是不包括string轉(zhuǎn)日期類(lèi)型,因?yàn)椴恢纒tring的具體格式。springmvc提供了很多的Converter,但是有些時(shí)候也可以定義自己的Converter用于字符串轉(zhuǎn)換特殊實(shí)體類(lèi)或者指定轉(zhuǎn)換的日期類(lèi)型的格式。

springmvc內(nèi)置的類(lèi)型參數(shù)有,HttpServletRequest HttpServletResponse HttpSession和Model/ModelMap,即這些參數(shù)不用我們手動(dòng)創(chuàng)建,springmvc會(huì)幫我們創(chuàng)建好,只需注入就行了。當(dāng)在方法的參數(shù)里面有這些類(lèi)型的參數(shù)時(shí),springmvc會(huì)自動(dòng)完成綁定。不難理解HttpServletRequest HttpServletResponse就是每次請(qǐng)求的request對(duì)象與response,我們可以使用這兩個(gè)對(duì)象處理請(qǐng)求與相應(yīng)信息。HttpSession就是session會(huì)話(huà)對(duì)象。至于Model需要說(shuō)明一下,Model只是一個(gè)接口實(shí)際傳入的是ModelMap對(duì)象,他的作用變相理解為request的域,因?yàn)橥鵐odelMap里面放的值最后都會(huì)添加進(jìn)request的域里面。

參數(shù)解析器

handler的返回值有專(zhuān)門(mén)的處理器,而handler的參數(shù)也有對(duì)應(yīng)的解析器,不同的參數(shù)會(huì)使用不同的解析器,解析器在處理器適配器的配置如下,以下是適配器默認(rèn)使用的一些解析器:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList();
        resolvers.add(new RequestParamMethodArgumentResolver(this.getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestPartMethodArgumentResolver(this.getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestHeaderMethodArgumentResolver(this.getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(this.getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(this.getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(this.getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
        resolvers.add(new ModelMethodProcessor());
        resolvers.add(new MapMethodProcessor());
        resolvers.add(new ErrorsMethodArgumentResolver());
        resolvers.add(new SessionStatusMethodArgumentResolver());
        resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
        if (this.getCustomArgumentResolvers() != null) {
            resolvers.addAll(this.getCustomArgumentResolvers());
        }

        resolvers.add(new RequestParamMethodArgumentResolver(this.getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));
        return resolvers;
    }

當(dāng)處理器映射器返回handler后,交給處理器適配器處理,處理器適配器執(zhí)行handler之前,會(huì)將handler的所有參數(shù)提取出來(lái)封裝成一個(gè)參數(shù)集合,然后每個(gè)參數(shù)會(huì)使用解析器對(duì)handler的參數(shù)進(jìn)行實(shí)參綁定(通過(guò)在request獲取,并根據(jù)不同的參數(shù)類(lèi)型使用對(duì)應(yīng)的解析器),然后再執(zhí)行handler的時(shí)候參數(shù)已經(jīng)完成綁定。

參數(shù)解析器都要實(shí)現(xiàn)HandlerMethodArgumentResolver接口,最重要的是以下兩個(gè)方法。

public interface HandlerMethodArgumentResolver {
    //定義解析器支持的參數(shù)類(lèi)型的判別條件
    boolean supportsParameter(MethodParameter parameter);

    //對(duì)于匹配成功的參數(shù)具體的解析操作。
    @Nullable
    Object resolveArgument(MethodParameter parameter, 
                          @Nullable ModelAndViewContainer mavContainer,
                          NativeWebRequest webRequest, 
                          @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

RequestParamMethodArgumentResolver

RequestParamMethodArgumentResolver是使用的比較多的參數(shù)解析器,以下是對(duì)其兩個(gè)核心方法的講解。

(1)RequestParamMethodArgumentResolver的supportsParameter(MethodParameter parameter):

該方法用于判斷支持的類(lèi)型,傳入的MethodParameter就是一個(gè)封裝了參數(shù)相關(guān)信息的方法參數(shù)對(duì)象。類(lèi)型匹配如下:

仔細(xì)的話(huà),可以發(fā)現(xiàn)添加參數(shù)解析器的時(shí)候添加了兩個(gè)RequestParamMethodArgumentResolver對(duì)象,唯一的區(qū)別是屬性u(píng)seDefaultResolution為true還是false。
第一個(gè)RequestParamMethodArgumentResolver匹配@RequestParam注解的參數(shù)以及MultipartFile類(lèi)型的參數(shù)。
第二個(gè)RequestParamMethodArgumentResolver匹配一些簡(jiǎn)單類(lèi)型,比如String,CharSequence, Number,Date類(lèi)型的參數(shù)。

總的來(lái)說(shuō),如果忽略先后順序的話(huà)可以把兩個(gè)RequestParamMethodArgumentResolver看成一個(gè)整體,支持@RequestParam注解的參數(shù)、MultipartFile類(lèi)型、String,CharSequence, Number,Date類(lèi)型的參數(shù)。

(2)Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;

該方法主要就從request里獲取參數(shù)值并封裝成與handler方法的參數(shù)的同一類(lèi)型的對(duì)象并返回。需要注意的是,從request里獲取到string值后,是通過(guò)WebDataBinderFactory對(duì)象獲取WebDataBinder,然后使用WebDataBinder將request里對(duì)應(yīng)的值轉(zhuǎn)換成與參數(shù)同類(lèi)型的對(duì)象的。

@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    MethodParameter nestedParameter = parameter.nestedIfOptional();

    Object resolvedName = resolveStringValue(namedValueInfo.name);
    if (resolvedName == null) {
        throw new IllegalArgumentException(
                "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
    }

    //這一步主要作用就是從request里獲取參數(shù)對(duì)應(yīng)的值,需要注意的是這里都是string類(lèi)型的
    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    if (arg == null) {
        if (namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }
        else if (namedValueInfo.required && !nestedParameter.isOptional()) {
            handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
        arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    }
    else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
        arg = resolveStringValue(namedValueInfo.defaultValue);
    }

    if (binderFactory != null) {
        //使用使用WebDataBinderFactory創(chuàng)建WebDataBinder
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            //將String類(lèi)型的值轉(zhuǎn)換成于參數(shù)同類(lèi)型的值,如果參數(shù)是Integer,這里就返回一個(gè)Integer的值。
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
        catch (ConversionNotSupportedException ex) {
            throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        catch (TypeMismatchException ex) {
            throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
    }
    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

    return arg;
}

重要對(duì)象

1 WebDataBinderFactory

該對(duì)象主要用于創(chuàng)建WebDataBinder對(duì)象。它有個(gè)WebBindingInitializer類(lèi)型的屬性(該屬性的值來(lái)自處理器適配器的WebBindingInitializer類(lèi)型的屬性值),WebDataBinderFactory使用WebBindingInitializer在創(chuàng)建WebDataBinder后完成WebDataBinder的初始化(主要是完成ConversionService的賦值)。

初始化WebDataBinder的ConversionService

@Override
    public void initBinder(WebDataBinder binder) {
        binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
        if (this.directFieldAccess) {
            binder.initDirectFieldAccess();
        }
        if (this.messageCodesResolver != null) {
            binder.setMessageCodesResolver(this.messageCodesResolver);
        }
        if (this.bindingErrorProcessor != null) {
            binder.setBindingErrorProcessor(this.bindingErrorProcessor);
        }
        if (this.validator != null && binder.getTarget() != null &&
                this.validator.supports(binder.getTarget().getClass())) {
            binder.setValidator(this.validator);
        }
        //將WebBindingInitializer的ConversionService傳給WebDataBinder
        if (this.conversionService != null) {
            binder.setConversionService(this.conversionService);
        }
        if (this.propertyEditorRegistrars != null) {
            for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
                propertyEditorRegistrar.registerCustomEditors(binder);
            }
        }
    }

2 WebDataBinder

WebDataBinder對(duì)象有個(gè)SimpleTypeConverter類(lèi)型的屬性,最終使用的就是該類(lèi)型的屬性完成類(lèi)型的轉(zhuǎn)換。

WebDataBinder主要通過(guò)convertIfNecessary方法獲取與參數(shù)對(duì)應(yīng)類(lèi)型的值。

3 SimpleTypeConverter

有兩個(gè)重要的屬性:TypeConverterDelegate類(lèi)型的屬性和ConversionService類(lèi)型的屬性。

實(shí)際調(diào)用的是TypeConverterSupport->doConvert,重點(diǎn)研究doConvert,經(jīng)過(guò)層層遞歸,最重要的就是doConvert完成參數(shù)的自動(dòng)類(lèi)型綁定,然后在里面繼續(xù)調(diào)用TypeConverterDelegate屬性的convertIfNecessary方法。

@Nullable
private <T> T doConvert(@Nullable Object value,@Nullable Class<T> requiredType,
            @Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException {

        Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
        try {
            if (field != null) {
                return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
            }
            else {
                return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
            }
        }
        catch (ConverterNotFoundException | IllegalStateException ex) {
            throw new ConversionNotSupportedException(value, requiredType, ex);
        }
        catch (ConversionException | IllegalArgumentException ex) {
            throw new TypeMismatchException(value, requiredType, ex);
        }
    }

最終的方法,該方法返回的就是轉(zhuǎn)換后的值。

    @Nullable
    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;

        // 獲取ConversionService,它有個(gè)轉(zhuǎn)換器集合,有很多的轉(zhuǎn)換器。
        ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
            TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
            //conversionService有很多轉(zhuǎn)換器,這里判斷是否有支持該類(lèi)型的轉(zhuǎn)換器。
            if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                try {
                    //開(kāi)始轉(zhuǎn)換
                    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?
        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;
    }

4 ConversionService(使用的實(shí)例是GenericConversionService)

前面的對(duì)象可以粗略的了解,經(jīng)過(guò)重重遞歸,發(fā)現(xiàn)重要的就是該對(duì)象。

該對(duì)象有個(gè)GenericConversionService.Converters類(lèi)型的屬性,這個(gè)屬性有個(gè)HashMap類(lèi)型的屬性,名為Converters,如下圖所示。


image.png
private final Map<ConvertiblePair, GenericConversionService.ConvertersForPair> converters;

1)key為ConvertiblePair,可以簡(jiǎn)單的理解為源數(shù)據(jù)類(lèi)型與目標(biāo)數(shù)據(jù)類(lèi)型,只要這兩個(gè)相同,key就相等。目前spring內(nèi)置的converters基本把所有類(lèi)型的key都包含了進(jìn)去。

2)value是GenericConversionService.ConvertersForPair類(lèi)型,它內(nèi)部有個(gè)GenericConverter的轉(zhuǎn)換器集合,用于存儲(chǔ)具有相同key的轉(zhuǎn)換器(當(dāng)我們添加的一個(gè)轉(zhuǎn)換器是,它就會(huì)放在對(duì)應(yīng)key下的GenericConverter集合的第一個(gè),順序越靠前,優(yōu)先級(jí)越高)。

private static class ConvertersForPair {
        private final LinkedList<GenericConverter> converters;
....
}

如下圖所示,默認(rèn)的map里面的GenericConversionService.ConvertersForPair有124個(gè),然后每個(gè)GenericConversionService.ConvertersForPair里面都至少有一個(gè)Converter。

屬性:


ConversionService結(jié)構(gòu)

現(xiàn)在知道了所有的Converter所在的位置了以后,接下來(lái)就是關(guān)于知道源類(lèi)型與目標(biāo)類(lèi)型后,如何找到與之匹配的轉(zhuǎn)換器Converter。

匹配步驟:
1 通過(guò)源數(shù)據(jù)類(lèi)型與目標(biāo)類(lèi)型的key找到對(duì)應(yīng)的GenericConversionService.ConvertersForPair。

2 然后通過(guò)GenericConversionService.ConvertersForPair獲取對(duì)應(yīng)的Converter(這里會(huì)Converter會(huì)進(jìn)一步判斷是否支持(matchs)源數(shù)據(jù)類(lèi)型與目標(biāo)類(lèi)型,如果不支持將返回null,即沒(méi)有對(duì)應(yīng)的Converter)。提示一下,一般通過(guò)Converter<S,T>接口實(shí)現(xiàn)的Converter的matchs都是true,因?yàn)镃onverter<S,T>與ConditionalConverter沒(méi)有關(guān)系,沒(méi)有對(duì)應(yīng)的matchs方法,

4.1 canConvert方法

判斷規(guī)則就是能通過(guò)源類(lèi)型與目標(biāo)類(lèi)型獲取到Converter就可以轉(zhuǎn)換,如果不能獲取,就不可以轉(zhuǎn)換。

//sourceType傳入?yún)?shù)類(lèi)型,需要轉(zhuǎn)換從目標(biāo)類(lèi)型targetType
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 {
            //獲取轉(zhuǎn)換器
            GenericConverter converter = this.getConverter(sourceType, targetType);
            return converter != null;
        }
    }

4.2 getConverter方法

@Nullable
//獲取converter
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
        
    GenericConversionService.ConverterCacheKey key = new GenericConversionService.ConverterCacheKey(sourceType, targetType);
    GenericConverter converter = (GenericConverter)this.converterCache.get(key);
    if (converter != null) {
        return converter != NO_MATCH ? converter : null;
    } else {
        //主要是通過(guò)該方法獲取 
        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;
        }
    }
}

4.3 GenericConversionService.Converters.find方法

這里會(huì)將源類(lèi)型本身何其所有父類(lèi)接口都獲取,目標(biāo)類(lèi)型也是同樣,然后按照從小到大組合成key去獲取對(duì)應(yīng)的convertiblePair,然后通過(guò)convertiblePair獲取匹配的converter。比如源類(lèi)型是String,目標(biāo)類(lèi)型是Date,那么獲取到的可接受的類(lèi)型如下:


image.png
@Nullable
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
   //獲取源類(lèi)型可以轉(zhuǎn)換成的類(lèi)型(這些類(lèi)型就是源類(lèi)型實(shí)現(xiàn)的接口或者繼承的類(lèi))
   List<Class<?>> sourceCandidates = this.getClassHierarchy(sourceType.getType());
   //獲取目標(biāo)類(lèi)型可以轉(zhuǎn)換成的類(lèi)型(這些類(lèi)型就是目標(biāo)類(lèi)型實(shí)現(xiàn)的接口或者繼承的類(lèi))
   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();
           ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
           //判斷convertiblePair 的converter集合里的object是否匹配(都有個(gè)ObjectToObjectConverter打底)
           GenericConverter converter = this.getRegisteredConverter(sourceType, targetType, convertiblePair);
           if (converter != null) {
               return converter;
           }
       }
   }

   return null;
}

4.4 GenericConversionService.Converters.getRegisteredConverter方法

@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType, TypeDescriptor targetType, ConvertiblePair convertiblePair) {
     //通過(guò)Key從converters集合里面獲取convertersForPair,然后由convertersForPair獲取converter
     //converters里面存儲(chǔ)的都是GenericConversionService.ConvertersForPair類(lèi)型
     GenericConversionService.ConvertersForPair convertersForPair = (GenericConversionService.ConvertersForPair)this.converters.get(convertiblePair);
     if (convertersForPair != null) {
         //進(jìn)一步判斷GenericConverter是否匹配(通過(guò)調(diào)用matchs),匹配成功才能返回GenericConverter 
         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;
}

4.5 convert方法

@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 {
            //根據(jù)資源類(lèi)型與目標(biāo)類(lèi)型獲取converter(日期對(duì)象獲取的是ObjectToObjectConverter類(lèi)型的converter)
            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);
            }
        }
    }

接收日期字符串

最后發(fā)現(xiàn)converters里面本身自帶一個(gè)String轉(zhuǎn)Date的convertersForPair,這個(gè)convertersForPair自帶一個(gè)Converter,該Converter的匹配規(guī)則(matchs)是必須使用@DateTimeFormat(pattern = "yyyy-MM-dd")指定日期參數(shù)或?qū)嶓w屬性。使用示例:

package com.dahuici.zyb.entity;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;

@Data
public class User {

    private String name;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date date;


}
    @GetMapping("/login")
    public String helloword(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
        System.out.println(date);

        return "success";
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容