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

    接上一篇:spring boot 是如何利用jackson进行反序列化的?

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

    返回的search是如何序列化json的?

    上一篇说到RequestResponseBodyMethodProcessor这个类在json序列化和反序列化都中很重要:

    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;
        }

    上面是反序列化时用的。

    @Override
        public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
                throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    
            mavContainer.setRequestHandled(true);
            ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
            ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
    
            // Try even with null return value. ResponseBodyAdvice could get involved.
            writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
        }

    上面是序列化时用到的。

    当然也可以F7一点一点的debug也会进入到这里。

    然后继续:

    for (HttpMessageConverter<?> converter : this.messageConverters) {
                    GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                            (GenericHttpMessageConverter<?>) converter : null);
                    if (genericConverter != null ?
                            ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                            converter.canWrite(valueType, selectedMediaType)) {
                        body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                                (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                                inputMessage, outputMessage);
                        if (body != null) {
                            Object theBody = body;
                            LogFormatUtils.traceDebug(logger, traceOn ->
                                    "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                            addContentDispositionHeader(inputMessage, outputMessage);
                            if (genericConverter != null) {
                                genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                            }
                            else {
                                ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                            }
                        }
                        else {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Nothing to write: null body");
                            }
                        }
                        return;
                    }
                }

    又走了这样的循环城,上篇中也介绍到了类型的循环,它就是要找到个们的jackson:

    org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

    下面的步骤太多,我就把调用的堆栈截图出来:

     看第一行,又是BeanSerielizer

    public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider)
            throws IOException
        {
            if (_objectIdWriter != null) {
                gen.setCurrentValue(bean); // [databind#631]
                _serializeWithObjectId(bean, gen, provider, true);
                return;
            }
            gen.writeStartObject(bean);
            if (_propertyFilterId != null) {
                serializeFieldsFiltered(bean, gen, provider);
            } else {
                serializeFields(bean, gen, provider);
            }
            gen.writeEndObject();
        }

    开始序列化每个属性:

    protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
            throws IOException
        {
            final BeanPropertyWriter[] props;
            if (_filteredProps != null && provider.getActiveView() != null) {
                props = _filteredProps;
            } else {
                props = _props;
            }
            int i = 0;
            try {
                for (final int len = props.length; i < len; ++i) {
                    BeanPropertyWriter prop = props[i];
                    if (prop != null) { // can have nulls in filtered list
                        prop.serializeAsField(bean, gen, provider);
                    }
                }
                if (_anyGetterWriter != null) {
                    _anyGetterWriter.getAndSerialize(bean, gen, provider);
                }
            } catch (Exception e) {
                String name = (i == props.length) ? "[anySetter]" : props[i].getName();
                wrapAndThrow(provider, e, bean, name);
            } catch (StackOverflowError e) {
                // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many
                //   stack frames to spare... just one or two; can't make many calls.
    
                // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly:
                //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
                JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
    
                 String name = (i == props.length) ? "[anySetter]" : props[i].getName();
                mapE.prependPath(new JsonMappingException.Reference(bean, name));
                throw mapE;
            }
        }
    public void serializeAsField(Object bean, JsonGenerator gen,
                SerializerProvider prov) throws Exception {
            // inlined 'get()'
            final Object value = (_accessorMethod == null) ? _field.get(bean)
                    : _accessorMethod.invoke(bean, (Object[]) null);
    
            // Null handling is bit different, check that first
            if (value == null) {
                if (_nullSerializer != null) {
                    gen.writeFieldName(_name);
                    _nullSerializer.serialize(null, gen, prov);
                }
                return;
            }
            // then find serializer to use
            JsonSerializer<Object> ser = _serializer;
            if (ser == null) {
                Class<?> cls = value.getClass();
                PropertySerializerMap m = _dynamicSerializers;
                ser = m.serializerFor(cls);
                if (ser == null) {
                    ser = _findAndAddDynamic(m, cls, prov);
                }
            }
            // and then see if we must suppress certain values (default, empty)
            if (_suppressableValue != null) {
                if (MARKER_FOR_EMPTY == _suppressableValue) {
                    if (ser.isEmpty(prov, value)) {
                        return;
                    }
                } else if (_suppressableValue.equals(value)) {
                    return;
                }
            }
            // For non-nulls: simple check for direct cycles
            if (value == bean) {
                // three choices: exception; handled by call; or pass-through
                if (_handleSelfReference(bean, gen, prov, ser)) {
                    return;
                }
            }
            gen.writeFieldName(_name);
            if (_typeSerializer == null) {
                ser.serialize(value, gen, prov);
            } else {
                ser.serializeWithType(value, gen, prov, _typeSerializer);
            }
        }

    这里用到的序列化的类是com.fasterxml.jackson.databind.ser.std.EnumSerializer,是不是和上一篇很像,其实序列化和反序列化的原理和过程真的很像。

    这里由于属性是用的是枚举,但序列化后是汉字:

     如果想序列化成数字呢?

    我的枚举是这样的:

    @Getter
    @AllArgsConstructor
    //@JsonDeserialize(using = BaseEnumDeserializer.class)
    public enum EnumBookingState implements BaseEnum {
    
        订购中(1),
        订购完成(2);
        //
    //@JsonValue
    private final int value; }

    很简单,只需要在value上中上@JsonValue,但它的原理是什么呢?

    同样是上面的截图,但是序列化的工具就是不一样了:

    public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException
        {
            try {
                Object value = _accessor.getValue(bean);
                if (value == null) {
                    prov.defaultSerializeNull(gen);
                    return;
                }
                JsonSerializer<Object> ser = _valueSerializer;
                if (ser == null) {
                    Class<?> c = value.getClass();
                    /* 10-Mar-2010, tatu: Ideally we would actually separate out type
                     *   serializer from value serializer; but, alas, there's no access
                     *   to serializer factory at this point... 
                     */
                    // let's cache it, may be needed soon again
                    ser = prov.findTypedValueSerializer(c, true, _property);
                }
                ser.serialize(value, gen, prov);
            } catch (Exception e) {
                wrapAndThrow(prov, e, bean, _accessor.getName() + "()");
            }
        }

    为什么打上了标签就用的是JsonValueSerializer?

    protected final JsonSerializer<?> findSerializerByAnnotations(SerializerProvider prov, 
                JavaType type, BeanDescription beanDesc)
            throws JsonMappingException
        {
            Class<?> raw = type.getRawClass();
            // First: JsonSerializable?
            if (JsonSerializable.class.isAssignableFrom(raw)) {
                return SerializableSerializer.instance;
            }
            // Second: @JsonValue for any type
            AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor();
            if (valueAccessor != null) {
                if (prov.canOverrideAccessModifiers()) {
                    ClassUtil.checkAndFixAccess(valueAccessor.getMember(),
                            prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
                }
                JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueAccessor);
                return new JsonValueSerializer(valueAccessor, ser);
            }
            // No well-known annotations...
            return null;
        }

    具体的调用调用太多,还是把堆栈信息拿出来:

    整个的调用过程与反序列化的过程几乎一样。

    下面这个类是一个主要的过程 :

    protected JsonSerializer<?> _createSerializer2(SerializerProvider prov,
                JavaType type, BeanDescription beanDesc, boolean staticTyping)
            throws JsonMappingException
        {
            JsonSerializer<?> ser = null;
            final SerializationConfig config = prov.getConfig();
            
            // Container types differ from non-container types
            // (note: called method checks for module-provided serializers)
            if (type.isContainerType()) {
                if (!staticTyping) {
                    staticTyping = usesStaticTyping(config, beanDesc, null);
                }
                // 03-Aug-2012, tatu: As per [databind#40], may require POJO serializer...
                ser =  buildContainerSerializer(prov, type, beanDesc, staticTyping);
                // Will return right away, since called method does post-processing:
                if (ser != null) {
                    return ser;
                }
            } else {
                if (type.isReferenceType()) {
                    ser = findReferenceSerializer(prov, (ReferenceType) type, beanDesc, staticTyping);
                } else {
                    // Modules may provide serializers of POJO types:
                    for (Serializers serializers : customSerializers()) {
                        ser = serializers.findSerializer(config, type, beanDesc);
                        if (ser != null) {
                            break;
                        }
                    }
                }
                // 25-Jun-2015, tatu: Then JsonSerializable, @JsonValue etc. NOTE! Prior to 2.6,
                //    this call was BEFORE custom serializer lookup, which was wrong.
                if (ser == null) {
                    ser = findSerializerByAnnotations(prov, type, beanDesc);
                }
            }
            
            if (ser == null) {
                // Otherwise, we will check "primary types"; both marker types that
                // indicate specific handling (JsonSerializable), or main types that have
                // precedence over container types
                ser = findSerializerByLookup(type, config, beanDesc, staticTyping);
                if (ser == null) {
                    ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping);
                    if (ser == null) {
                        // And this is where this class comes in: if type is not a
                        // known "primary JDK type", perhaps it's a bean? We can still
                        // get a null, if we can't find a single suitable bean property.
                        ser = findBeanSerializer(prov, type, beanDesc);
                        // Finally: maybe we can still deal with it as an implementation of some basic JDK interface?
                        if (ser == null) {
                            ser = findSerializerByAddonType(config, type, beanDesc, staticTyping);
                            // 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get
                            //   'unknown' serializer assigned earlier, here, so that it gets properly
                            //   post-processed
                            if (ser == null) {
                                ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass());
                            }
                        }
                    }
                }
            }
            if (ser != null) {
                // [databind#120]: Allow post-processing
                if (_factoryConfig.hasSerializerModifiers()) {
                    for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
                        ser = mod.modifySerializer(config, beanDesc, ser);
                    }
                }
            }
            return ser;
        }

    这个方法名,就告诉我们答案了:findSerializerByAnnotations

  • 相关阅读:
    PHPStorm打开文件所在目录
    登录顺序图与项目部署图
    JAVA配置Tomcat
    设计模式与足球
    JAVA设计模式之【模板方法模式】
    JAVA设计模式之【策略模式】
    JAVA设计模式之【状态模式】
    JAVA设计模式之【观察者模式】
    phpstorm 或 webstorm 设置打开多个项目,多个项目并存。
    JAVA设计模式之【迭代器模式】
  • 原文地址:https://www.cnblogs.com/hankuikui/p/11593779.html
Copyright © 2011-2022 走看看