@JsonFormat 实现原理

很多人都会用@DateTimeFormat@JsonFormat,来处理前后端的时间转换,举个栗子:
Controller 层:

package com.tongbanjie.bootdemo.web;

import com.tongbanjie.bootdemo.params.QueryParams;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Lu. Yan
 * @create 2019-06-01
 */
@RestController
public class HelloWorldController {

    @GetMapping("/hello-world")
    public QueryParams helloWorld(@RequestBody QueryParams queryParams){
        return queryParams;
    }
}

实体类QueryParams

package com.tongbanjie.bootdemo.params;

import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

/**
 * @author Lu. Yan
 * @create 2019-06-07
 */
public class QueryParams {

    /**
     * 开始时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH")
    private Date startTime;

    /**
     * 名称
     */
    private String name;

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

当前端采用 Content-Type为application/json,传输数据时,如:

{
    "name":"ee",
    "startTime":"2019-05-12 12"
}

我们这里的@JsonFormat就能将startTime转换为 Date 类型,无需我们自己单独处理。那么它是怎么实现的呢?接下来我们来一起探讨这个奥秘。

有一点需要注意的是,对于转换前端传过来的时间,@JsonFormat只适合 Content-Type 为application/json的请求,如果是表单请求,请采用@DateTimeFormat。我们知道,一个 Http 请求过来,会通过InvocableHandlerMethod#invokeForRequest方法来处理这个请求,并返回结果:

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(this.getMethod(), this.getBeanType()) + "' with arguments " + Arrays.toString(args));
        }

        Object returnValue = this.doInvoke(args);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Method [" + ClassUtils.getQualifiedMethodName(this.getMethod(), this.getBeanType()) + "] returned [" + returnValue + "]");
        }

        return returnValue;
    }

getMethodArgumentValues(request, mavContainer, providedArgs)方法正好是获取方法请求参数值的,于是我们猜想,时间 Format 转换应该在这里面完成的,我们不妨继续往下看:

private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        MethodParameter[] parameters = this.getMethodParameters();
        Object[] args = new Object[parameters.length];

        for(int i = 0; i < parameters.length; ++i) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = this.resolveProvidedArgument(parameter, providedArgs);
            if (args[i] == null) {
                if (this.argumentResolvers.supportsParameter(parameter)) {
                    try {
                        args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var9) {
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug(this.getArgumentResolutionErrorMessage("Failed to resolve", i), var9);
                        }

                        throw var9;
                    }
                } else if (args[i] == null) {
                    throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() + ": " + this.getArgumentResolutionErrorMessage("No suitable resolver for", i));
                }
            }
        }

处理请求参数的逻辑在this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)这个方法里,该方法会选择对应的HandlerMethodArgumentResolver来处理,由于我使用@RequestBody来接收请求参数的,于是HandlerMethodArgumentResolverRequestResponseBodyMethodProcessor;如果是表单接收,则是ServletModelAttributeMethodProcessor。所以我们继续看RequestResponseBodyMethodProcessor#resolveArgument方法:

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

        parameter = parameter.nestedIfOptional();
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        String name = Conventions.getVariableNameForParameter(parameter);

        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }

        return adaptArgumentIfNecessary(arg, parameter);
    }

    protected  Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
            Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
        if (arg == null && checkRequired(parameter)) {
            throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getExecutable().toGenericString());
        }
        return arg;
    }

该方法中,会将HttpServletRequest转化为ServletServerHttpRequest(请求参数在里面),然后调用AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(HttpInputMessage, MethodParameter, Type)来处理请求参数:

protected  Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        MediaType contentType;
        boolean noContentType = false;
        try {
            contentType = inputMessage.getHeaders().getContentType();
        }
        catch (InvalidMediaTypeException ex) {
            throw new HttpMediaTypeNotSupportedException(ex.getMessage());
        }
        if (contentType == null) {
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
        }

        Class contextClass = parameter.getContainingClass();
        Class targetClass = (targetType instanceof Class ? (Class) targetType : null);
        if (targetClass == null) {
            ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
            targetClass = (Class) resolvableType.resolve();
        }

        HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
        Object body = NO_VALUE;

        EmptyBodyCheckingHttpInputMessage message;
        try {
            message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

            for (HttpMessageConverter converter : this.messageConverters) {
                Class> converterType = (Class>) converter.getClass();
                GenericHttpMessageConverter genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter) converter : null);
                if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                        (targetClass != null && converter.canRead(targetClass, contentType))) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
                    }
                    if (message.hasBody()) {
                        HttpInputMessage msgToUse =
                                getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                ((HttpMessageConverter) converter).read(targetClass, msgToUse));
                        body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                    }
                    else {
                        body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                    }
                    break;
                }
            }
        }
        ...
        return body;
    }

该方法会遍历消息转换器messageConverters,来处理请求参数中的每一个参数,我们先看总共有多少个消息转换器:


它首先会转化为 Http的消息转换器GenericHttpMessageConverter,在这8个messageConverters中,只有MappingJackson2HttpMessageConverterGenericHttpMessageConverter,于是我们直接看MappingJackson2HttpMessageConverter#canRead(Type, Class,MediaType)方法:

    public boolean canRead(Type type, @Nullable Class contextClass, @Nullable MediaType mediaType) {
        if (!canRead(mediaType)) {
            return false;
        }
        JavaType javaType = getJavaType(type, contextClass);
        AtomicReference causeRef = new AtomicReference<>();
        if (this.objectMapper.canDeserialize(javaType, causeRef)) {
            return true;
        }
        logWarningIfNecessary(javaType, causeRef.get());
        return false;
    }

该方法是验证请求参数是否能被反序列化,我们直接看里面设置反序列化器的方法DeserializerCache#_createAndCache2方法:

protected JsonDeserializer _createAndCache2(DeserializationContext ctxt,
            DeserializerFactory factory, JavaType type)
        throws JsonMappingException
    {
        JsonDeserializer deser;
        try {
            deser = _createDeserializer(ctxt, factory, type);
        } catch (IllegalArgumentException iae) {
            // We better only expose checked exceptions, since those
            // are what caller is expected to handle
            throw JsonMappingException.from(ctxt, iae.getMessage(), iae);
        }
        if (deser == null) {
            return null;
        }
        /* cache resulting deserializer? always true for "plain" BeanDeserializer
         * (but can be re-defined for sub-classes by using @JsonCachable!)
         */
        // 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desers
        boolean addToCache = !_hasCustomHandlers(type) && deser.isCachable();

        /* we will temporarily hold on to all created deserializers (to
         * handle cyclic references, and possibly reuse non-cached
         * deserializers (list, map))
         */
        /* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental
         *   resolution of a reference -- couple of ways to prevent this;
         *   either not add Lists or Maps, or clear references eagerly.
         *   Let's actually do both; since both seem reasonable.
         */
        /* Need to resolve? Mostly done for bean deserializers; required for
         * resolving cyclic references.
         */
        if (deser instanceof ResolvableDeserializer) {
            _incompleteDeserializers.put(type, deser);
            ((ResolvableDeserializer)deser).resolve(ctxt);
            _incompleteDeserializers.remove(type);
        }
        if (addToCache) {
            _cachedDeserializers.put(type, deser);
        }
        return deser;
    }
 
 

首先它会创建一个反序列化器_createDeserializer(ctxt, factory, type),然后通过这个反序列化器来处理请求参数((ResolvableDeserializer)deser).resolve(ctxt)。在创建反序列化器的过程中,它会给每个请求参数生成一个MethodProperty对象,用来封装请求参数的信息,如参数名称,参数类型,注解信息等等,感兴趣的小伙伴可以自己往下看,由于篇幅原因我就不累赘了。我们直接看它处理请求参数的逻辑:

public void resolve(DeserializationContext ctxt) throws JsonMappingException {
  ...    
  for (SettableBeanProperty origProp : _beanProperties) {
            SettableBeanProperty prop = origProp;
            JsonDeserializer deser = prop.getValueDeserializer();
            deser = ctxt.handlePrimaryContextualization(deser, prop, prop.getType());
            prop = prop.withValueDeserializer(deser);
            // Need to link managed references with matching back references
            prop = _resolveManagedReferenceProperty(ctxt, prop);
    }
    ...
}

该方法会遍历SettableBeanProperty集合,SettableBeanPropertyMethodProperty的父类。然后调用DeserializationContext#handlePrimaryContextualization方法:

public JsonDeserializer handlePrimaryContextualization(JsonDeserializer deser,
            BeanProperty prop, JavaType type)
        throws JsonMappingException
    {
        if (deser instanceof ContextualDeserializer) {
            _currentType = new LinkedNode(type, _currentType);
            try {
                deser = ((ContextualDeserializer) deser).createContextual(this, prop);
            } finally {
                _currentType = _currentType.next();
            }
        }
        return deser;
    }

该方法通过根据主属性反序列化器JsonDeserializer(该反序列化器还不是完整版,没有注解信息的)来反序列化POJO属性的值,这里有个判断deser instanceof ContextualDeserializer,我们来看ContextualDeserializer是个什么东东:

package com.fasterxml.jackson.databind.deser;

import com.fasterxml.jackson.databind.*;

/**
 * Add-on interface that {@link JsonDeserializer}s can implement to get a callback
 * that can be used to create contextual (context-dependent) instances of
 * deserializer to use for  handling properties of supported type.
 * This can be useful
 * for deserializers that can be configured by annotations, or should otherwise
 * have differing behavior depending on what kind of property is being deserialized.
 *

* Note that in cases where deserializer needs both contextualization and * resolution -- that is, implements both this interface and {@link ResolvableDeserializer} * -- resolution via {@link ResolvableDeserializer} occurs first, and contextual * resolution (via this interface) later on. */ public interface ContextualDeserializer { /** * Method called to see if a different (or differently configured) deserializer * is needed to deserialize values of specified property. * Note that instance that this method is called on is typically shared one and * as a result method should NOT modify this instance but rather construct * and return a new instance. This instance should only be returned as-is, in case * it is already suitable for use. * * @param ctxt Deserialization context to access configuration, additional * deserializers that may be needed by this deserializer * @param property Method, field or constructor parameter that represents the property * (and is used to assign deserialized value). * Should be available; but there may be cases where caller cannot provide it and * null is passed instead (in which case impls usually pass 'this' deserializer as is) * * @return Deserializer to use for deserializing values of specified property; * may be this instance or a new instance. * * @throws JsonMappingException */ public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException; }

ContextualDeserializer是一个扩展接口,实现该接口来获得不同参数类型的反序列化器实例。例如PrimitiveArrayDeserializers是基本类型数组反序列化器,DateBasedDeserializer是时间类型反序列化器。但是注意,基本类型和 String 类型并不是ContextualDeserializer。所以我们的name属性类型是 String 并不会进入判断条件,startTime属性的类型是Date,故进入了判断条件。随后通过DateBasedDeserializer#createContextual来获取时间类型反序列化器实例:

public JsonDeserializer createContextual(DeserializationContext ctxt,
                BeanProperty property)
           throws JsonMappingException
        {
            final JsonFormat.Value format = findFormatOverrides(ctxt, property,
                    handledType());

            if (format != null) {
                TimeZone tz = format.getTimeZone();
                final Boolean lenient = format.getLenient();

                // First: fully custom pattern?
                if (format.hasPattern()) {
                    final String pattern = format.getPattern();
                    final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                    SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
                    if (tz == null) {
                        tz = ctxt.getTimeZone();
                    }
                    df.setTimeZone(tz);
                    if (lenient != null) {
                        df.setLenient(lenient);
                    }
                    return withDateFormat(df, pattern);
                }
            ...
            return this;
        }

  protected JsonFormat.Value findFormatOverrides(DeserializationContext ctxt,
            BeanProperty prop, Class typeForDefaults)
    {
        if (prop != null) {
            return prop.findPropertyFormat(ctxt.getConfig(), typeForDefaults);
        }
        // even without property or AnnotationIntrospector, may have type-specific defaults
        return ctxt.getDefaultPropertyFormat(typeForDefaults);
    }

在这个方法里,终于找到我们想看到的关键字了JsonFormat,我们应该迫不及待的看它的findFormatOverrides(ctxt, property,handledType())方法,因为它会找出参数上@JsonFormat注解,以便解析,由于我们的propertySettableBeanProperty类型,而SettableBeanProperty又是ConcreteBeanPropertyBase的子类,于是我们直接看ConcreteBeanPropertyBase#findPropertyFormat方法:

public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType)
    {
        // 15-Apr-2016, tatu: Let's calculate lazily, retain; assumption being however that
        //    baseType is always the same
        JsonFormat.Value v = _propertyFormat;
        if (v == null) {
            JsonFormat.Value v1 = config.getDefaultPropertyFormat(baseType);
            JsonFormat.Value v2 = null;
            AnnotationIntrospector intr = config.getAnnotationIntrospector();
            if (intr != null) {
                AnnotatedMember member = getMember();
                if (member != null) {
                    v2 = intr.findFormat(member);
                }
            }
            if (v1 == null) {
                v = (v2 == null) ? EMPTY_FORMAT : v2;
            } else {
                v = (v2 == null) ? v1 : v1.withOverrides(v2);
            }
            _propertyFormat = v;
        }
        return v;
    }

public final class MethodProperty extends SettableBeanProperty {
    private static final long serialVersionUID = 1;

    protected final AnnotatedMethod _annotated;

    public AnnotatedMember getMember() {  return _annotated; }

该方法就是来获取参数上@JsonFormat的值,getMember方法会取MethodProperty#_annotated属性值,于是就拿到了请求参数上的所有注解(可能多个),我们的startTime就一个@JsonFormat注解,然后通过findFormat(member)方法获取指定@JsonFormat注解信息。拿到注解信息之后,就可以继续在 DateBasedDeserializer#createContextual方法中,来生成时间类型的反序列化器实例DateBasedDeserializer#withDateFormat

        protected DateDeserializer withDateFormat(DateFormat df, String formatString) {
            return new DateDeserializer(this, df, formatString);
        }

生成的DateDeserializer赋值给SettableBeanProperty_valueDeserializer属性,以便下次使用。那么,哪里用到了生成的时间类型反序列化器DateDeserializer呢?直接看AbstractJackson2HttpMessageConverter#read方法:

public Object read(Type type, @Nullable Class contextClass, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {

        JavaType javaType = getJavaType(type, contextClass);
        return readJavaType(javaType, inputMessage);
    }

private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
        try {
            if (inputMessage instanceof MappingJacksonInputMessage) {
                Class deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
                if (deserializationView != null) {
                    return this.objectMapper.readerWithView(deserializationView).forType(javaType).
                            readValue(inputMessage.getBody());
                }
            }
            return this.objectMapper.readValue(inputMessage.getBody(), javaType);
        }
        catch (InvalidDefinitionException ex) {
            throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
        }
        catch (JsonProcessingException ex) {
            throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex);
        }
    }

public  T readValue(InputStream src, JavaType valueType)
        throws IOException, JsonParseException, JsonMappingException
    {
        return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
    } 

protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
        throws IOException
    {
        try (JsonParser p = p0) {
            Object result;
            JsonToken t = _initForReading(p, valueType);
            final DeserializationConfig cfg = getDeserializationConfig();
            final DeserializationContext ctxt = createDeserializationContext(p, cfg);
            if (t == JsonToken.VALUE_NULL) {
                // Ask JsonDeserializer what 'null value' to use:
                result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
            } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
                result = null;
            } else {
                JsonDeserializer deser = _findRootDeserializer(ctxt, valueType);
                if (cfg.useRootWrapping()) {
                    result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
                } else {
                    result = deser.deserialize(p, ctxt);
                }
                ctxt.checkUnresolvedObjectId();
            }
            if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
                _verifyNoTrailingTokens(p, ctxt, valueType);
            }
            return result;
        }
    }
 
 

先找到处理请求参数的根反序列化器,因为我们请求参数是使用对象接收的,于是根反序列化器为BeanDeserializer,于是我们直接看反序列化的操作 BeanDeserializer#deserialize

public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
    {
        // common case first
        if (p.isExpectedStartObjectToken()) {
            if (_vanillaProcessing) {
                return vanillaDeserialize(p, ctxt, p.nextToken());
            }
            // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
            //    what it is, including "expected behavior".
            p.nextToken();
            if (_objectIdReader != null) {
                return deserializeWithObjectId(p, ctxt);
            }
            return deserializeFromObject(p, ctxt);
        }
        return _deserializeOther(p, ctxt, p.getCurrentToken());
    }

public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException
    {
        ...
        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
        ...
        if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
            String propName = p.getCurrentName();
            do {
                p.nextToken();
                SettableBeanProperty prop = _beanProperties.find(propName);
                if (prop != null) { // normal case
                    try {
                        prop.deserializeAndSet(p, ctxt, bean);
                    } catch (Exception e) {
                        wrapAndThrow(e, bean, propName, ctxt);
                    }
                    continue;
                }
                handleUnknownVanilla(p, ctxt, bean, propName);
            } while ((propName = p.nextFieldName()) != null);
        }
        return bean;
    }

_valueInstantiator.createUsingDefault(ctxt)方法生成一个默认值的请求参数对象bean,然后遍历每个请求参数,调用prop.deserializeAndSet(p, ctxt, bean)方法,来进行反序列化并赋值操作,我们看startTime参数的反序列化过程,propMethodProperty,我们看其deserializeAndSet方法:

public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
            Object instance) throws IOException
    {
        Object value;
        if (p.hasToken(JsonToken.VALUE_NULL)) {
            if (_skipNulls) {
                return;
            }
            value = _nullProvider.getNullValue(ctxt);
        } else if (_valueTypeDeserializer == null) {
            value = _valueDeserializer.deserialize(p, ctxt);
        } else {
            value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
        }
        try {
            _setter.invoke(instance, value);
        } catch (Exception e) {
            _throwAsIOE(p, e, value);
        }
    }

该方法利用本参数的MethodProperty#_valueDeserializer反序列化器,来执行反序列化操作,由于参数startTime的反序列化器为DateDeserializer,于是我们直接看其deserialize方法:

        public java.util.Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return _parseDate(p, ctxt);
        }

      protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
            throws IOException
        {
            if (_customFormat != null) {
                if (p.hasToken(JsonToken.VALUE_STRING)) {
                    String str = p.getText().trim();
                    if (str.length() == 0) {
                        return (Date) getEmptyValue(ctxt);
                    }
                    synchronized (_customFormat) {
                        try {
                            return _customFormat.parse(str);
                        } catch (ParseException e) {
                            return (java.util.Date) ctxt.handleWeirdStringValue(handledType(), str,
                                    "expected format \"%s\"", _formatString);
                        }
                    }
                }
            }
            return super._parseDate(p, ctxt);
        }
    }

这里就会根据反序列化器的_customFormat属性(生成反序列化器时会赋值)来对请求参数进行 Format 转换。

转换完之后的值,继续调用_setter.invoke(instance, value) 方法,通过反射对指定 set 方法进行调用,从而对参数进行赋值。

至此,@JsonFormat实现原理解析完毕,总结下整体流程:

  1. AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(HttpInputMessage, MethodParameter,Type)读取请求参数的值时,调用AbstractJackson2HttpMessageConverter#canRead(Type, Class, MediaType)方法,对每个请求参数生成MethodPropery对象,该对象包括参数名称,参数类型,注解信息,反序列化器等信息。
  2. 接着AbstractJackson2HttpMessageConverter#read方法,通过MethodPropery对象信息,对参数的值进行转化,并使用反射对请求参数进行赋值。

你可能感兴趣的:(@JsonFormat 实现原理)