zoukankan      html  css  js  c++  java
  • spring boot 是如何利用jackson进行反序列化的?

    以下面的代码为例:

    @RestController
    public class HelloController {
    
        @RequestMapping("/")
        public BillSearch hello(@RequestBody BillSearch search) {
    
            return search;
        }
    }

    前端通过Postman进行模拟:

    下面开始一步步的揭开它的面纱:

    先从HandlerMethodArgumentResolverComposite开始:

        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
            HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
            if (resolver == null) {
                throw new IllegalArgumentException(
                        "Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
                                " supportsParameter should be called first.");
            }
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }

    resolver为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);
        }
    readWithMessageConverters方法如下:
    protected <T> 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(), inputMessage);
            }
            return arg;
        }
     
    protected <T> 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<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
            if (targetClass == null) {
                ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
                targetClass = (Class<T>) 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<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) 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 (message.hasBody()) {
                            HttpInputMessage msgToUse =
                                    getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                            body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                    ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                            body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                        }
                        else {
                            body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                        }
                        break;
                    }
                }
            }
            catch (IOException ex) {
                throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
            }
    
            if (body == NO_VALUE) {
                if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                        (noContentType && !message.hasBody())) {
                    return null;
                }
                throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
            }
    
            MediaType selectedContentType = contentType;
            Object theBody = body;
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
                return "Read "" + selectedContentType + "" to [" + formatted + "]";
            });
    
            return body;
        }
    上一篇博客里介绍了messageConverters,在项目启动时添加了MappingJackson2HttpMessageConverter,这里主是就是找到这个converter对参数进行解析:
    再进一步追踪:在AbstractJackson2HttpMessageConverter类中,就找到了我们要找到objectMapper:
    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, inputMessage);
            }
        }

    顺便再介绍一下objectMapper反序列化的主要步骤:

    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 {
    //com.fasterxml.jackson.databind.deser.BeanDeserializer JsonDeserializer
    <Object> 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; } }
    
    
    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());
        }
    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;

    针对这个属性进行反序列化解析,由于这个属性是个枚举,所以它的_valueDeserializer是com.fasterxml.jackson.databind.deser.std.EnumDeserializer

    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) {
           //com.fasterxml.jackson.databind.deser.std.EnumDeserializer value
    = _valueDeserializer.deserialize(p, ctxt); // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null if (value == null) { if (_skipNulls) { return; } value = _nullProvider.getNullValue(ctxt); } } else { value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); } try { _setter.invoke(instance, value); } catch (Exception e) { _throwAsIOE(p, e, value); } }

    那为什么jackson枚举的反序列化默认用的是EnumDeserializer呢?

    这要回到文章开始的地方说起:在一步中会判断指定的类型是否能够进行canRead()

    for (HttpMessageConverter<?> converter : this.messageConverters) {
                    Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) 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 (message.hasBody()) {
                            HttpInputMessage msgToUse =
                                    getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                            body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                    ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                            body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                        }
                        else {
                            body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                        }
                        break;
                    }
                }

    就从canRead()方法说起:

    public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
            if (!canRead(mediaType)) {
                return false;
            }
            JavaType javaType = getJavaType(type, contextClass);
            AtomicReference<Throwable> causeRef = new AtomicReference<>();
            if (this.objectMapper.canDeserialize(javaType, causeRef)) {
                return true;
            }
            logWarningIfNecessary(javaType, causeRef.get());
            return false;
        }
    public boolean canDeserialize(JavaType type, AtomicReference<Throwable> cause)
        {
            return createDeserializationContext(null,
                    getDeserializationConfig()).hasValueDeserializerFor(type, cause);
        }
    public boolean hasValueDeserializerFor(JavaType type, AtomicReference<Throwable> cause) {
            try {
                return _cache.hasValueDeserializerFor(this, _factory, type);
            } ...
        }
    public boolean hasValueDeserializerFor(DeserializationContext ctxt,
                DeserializerFactory factory, JavaType type)
            throws JsonMappingException
        {
            /* Note: mostly copied from findValueDeserializer, except for
             * handling of unknown types
             */
            JsonDeserializer<Object> deser = _findCachedDeserializer(type);
            if (deser == null) {
                deser = _createAndCacheValueDeserializer(ctxt, factory, type);
            }
            return (deser != null);
        }

    注意这个名称createAndCache它是会缓存的,也就是这个类型只会找一次,找到之后,就它的反序列化类就绑定了,缓存起来了,

    这们有时配置 jackson的objectMapper,可能会添加很多反序列化的模块,都会注册到_factoryConfig.deserializers()

    protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,
                DeserializationConfig config, BeanDescription beanDesc)
            throws JsonMappingException
        {
            for (Deserializers d  : _factoryConfig.deserializers()) {
                JsonDeserializer<?> deser = d.findBeanDeserializer(type, config, beanDesc);
                if (deser != null) {
                    return (JsonDeserializer<Object>) deser;
                }
            }
            return null;
        }
    private final JsonDeserializer<?> _find(JavaType type) {
            if (_classMappings == null) {
                return null;
            }
            return _classMappings.get(new ClassKey(type.getRawClass()));
        }
    每个模块都有_classMappings这样的集合,记录着类与它的序列化类的对应关系。由于我们没有定义这样的关系,那它就有一个默认的。

    再找到这个类的反序列化类以后,再开始找类里面的属性的:

    同样是在DeserializerCache类中的_createAndCache2方法:

    protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
                DeserializerFactory factory, JavaType type)
            throws JsonMappingException
        {
            JsonDeserializer<Object> 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, ClassUtil.exceptionMessage(iae), 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;
        }

    遍历每一个属性:

    for (SettableBeanProperty prop : _beanProperties) {
                if (!prop.hasValueDeserializer()) {
                    // [databind#125]: allow use of converters
                    JsonDeserializer<?> deser = findConvertingDeserializer(ctxt, prop);
                    if (deser == null) {
                        deser = ctxt.findNonContextualValueDeserializer(prop.getType());
                    }
                    SettableBeanProperty newProp = prop.withValueDeserializer(deser);
                    _replaceProperty(_beanProperties, creatorProps, prop, newProp);
                }
            }
    type.isEnumType()这里是关键:
    protected JsonDeserializer<?> _createDeserializer2(DeserializationContext ctxt,
                DeserializerFactory factory, JavaType type, BeanDescription beanDesc)
            throws JsonMappingException
        {
            final DeserializationConfig config = ctxt.getConfig();
            // If not, let's see which factory method to use:
            if (type.isEnumType()) {
                return factory.createEnumDeserializer(ctxt, type, beanDesc);
            }
            。。。
        }
    public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
                JavaType type, BeanDescription beanDesc)
            throws JsonMappingException
        {
            final DeserializationConfig config = ctxt.getConfig();
            final Class<?> enumClass = type.getRawClass();
            // 23-Nov-2010, tatu: Custom deserializer?
            JsonDeserializer<?> deser = _findCustomEnumDeserializer(enumClass, config, beanDesc);
    
            if (deser == null) {
                ValueInstantiator valueInstantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);
                SettableBeanProperty[] creatorProps = (valueInstantiator == null) ? null
                        : valueInstantiator.getFromObjectArguments(ctxt.getConfig());
                // May have @JsonCreator for static factory method:
    //这里是重点,如上面的注释,如果在enum中定义了工厂方法,找打上了
    JsonCreator的话,那就算指定了反序列化的方法了,会通过反射执行反序列化
                 for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
                    if (_hasCreatorAnnotation(ctxt, factory)) {
                        if (factory.getParameterCount() == 0) { // [databind#960]
                            deser = EnumDeserializer.deserializerForNoArgsCreator(config, enumClass, factory);
                            break;
                        }
                        Class<?> returnType = factory.getRawReturnType();
                        // usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
                        if (returnType.isAssignableFrom(enumClass)) {
                            deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory, valueInstantiator, creatorProps);
                            break;
                        }
                    }
                }
               
                // Need to consider @JsonValue if one found
                if (deser == null) {
                    deser = new EnumDeserializer(constructEnumResolver(enumClass,
                            config, beanDesc.findJsonValueAccessor()),
                            config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));
                }
            }
    
            // and then post-process it too
            if (_factoryConfig.hasDeserializerModifiers()) {
                for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
                    deser = mod.modifyEnumDeserializer(config, type, beanDesc, deser);
                }
            }
            return deser;
        }
    _findCustomEnumDeserializer又云objectMapper中的配置中找:由于没有配置,所以返回null
    protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,
                DeserializationConfig config, BeanDescription beanDesc)
            throws JsonMappingException
        {
            for (Deserializers d  : _factoryConfig.deserializers()) {
                JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);
                if (deser != null) {
                    return deser;
                }
            }
            return null;
        }

    由一没找到,所以就指定了EnumDeserializer为枚举的默认反序列化类了。

  • 相关阅读:
    编译 安装 infobright
    MySQL忘记密码恢复密码的实现方法
    Intel 服务器 架构 NUMA
    Centos 卸载 java
    vs2010 无法将文件“obj**”复制到“bin**”
    linux安装eclipse PyDev
    infobright 编译安装
    [转贴]==开手排车的八个绝招==
    [摘]广义企业级PDM系统下的PPM(工艺规划管理)
    中国皇帝定下佛教戒律:僧人不准吃肉
  • 原文地址:https://www.cnblogs.com/hankuikui/p/11592724.html
Copyright © 2011-2022 走看看