jackson源码分析

2020-06-10  本文已影响0人  无聊之园

背景:之前碰到序列化的问题,spring默认序列化又是jackson序列化,然后近来又爆出fastjson序列化的漏洞,所以打算看看jackson这个东西。

假设,如果我要写 一个序列化bean成json的序列化工具,那么可以这么做:
反射,获取一个bean的所有的成员变量名、变量类型,然后对于各种常用类型定义好序列化工具类,比如StringSerializer,ByteSerializer, IntegerSerializer,BeanSerializer,然后获取bean的不同的属性类型,选择不同的序列化工具类,把属性转成json字符串。比如,String类型,则直接转成"string",localDateTime,则根据默认的转换格式转成string,如果是bean,则递归调用。如此,自然就可以做一个json序列化。

这里分析的代码是j'ackson-datatype 2.6.0版本,高版本会不一样。

        ObjectMapper mapper = new ObjectMapper();
        Person person = new Person();
        person.setName("Tom");
        person.setAge(40);
        person.setNow(LocalDateTime.now());
 String jsonString = mapper.writeValueAsString(person);
        System.out.println(jsonString);
 public String writeValueAsString(Object value)
        throws JsonProcessingException
    {        
        // alas, we have to pull the recycler directly here...
//分段string 写入器,就是一个继承了了Writer的对象
        SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
        try {
// 关键这里,配置和写
            _configAndWriteValue(_jsonFactory.createGenerator(sw), value);
        } catch (JsonProcessingException e) { // to support [JACKSON-758]
            throw e;
        } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
            throw JsonMappingException.fromUnexpectedIOE(e);
        }
        return sw.getAndClear();
    }
protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException
    {
// JsonGenerator是WriterBasedJsonGenerator,而且传入了SegmentedStringWriter这个writer类
        WriterBasedJsonGenerator gen = new WriterBasedJsonGenerator(ctxt,
                _generatorFeatures, _objectCodec, out);
        if (_characterEscapes != null) {
// 配置转义符
            gen.setCharacterEscapes(_characterEscapes);
        }
        SerializableString rootSep = _rootValueSeparator;
        if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) {
            gen.setRootValueSeparator(rootSep);
        }
        return gen;
    }

这是ObjectMapper的方法

protected final void _configAndWriteValue(JsonGenerator g, Object value)
        throws IOException
    {
        SerializationConfig cfg = getSerializationConfig();
    //  把ObjectMapper的配置,配置到JsonGenerator中
       cfg.initialize(g); // since 2.5
。。。
        boolean closed = false;
        try {
// 创建和配置好serializerProvider,这里生成的是DefaultSerializerProvider的内部类,也是子类Impl,然后调用serializerProvider的serialize方法,传入JsonGenerator和序列化对象value
// 这里会真正序列化对象
            _serializerProvider(cfg).serializeValue(g, value);
...
        } finally {
        ...
    }

DefaultSerializerProvider的serializeValue方法

  public void serializeValue(JsonGenerator gen, Object value) throws IOException
    {
        if (value == null) {
            _serializeNull(gen);
            return;
        }
        Class<?> cls = value.getClass();
        // true, since we do want to cache root-level typed serializers (ditto for null property)
// 找到匹配这个类型的json序列化器
        final JsonSerializer<Object> ser = findTypedValueSerializer(cls, true, null);

找到匹配这个类型的json序列化器

 public JsonSerializer<Object> findTypedValueSerializer(Class<?> valueType,
            boolean cache, BeanProperty property)
        throws JsonMappingException
    {
        // 两阶段查找,先class 类hash映射,去_knownSerializers预先定义好的序列化集合中找寻是否有这个类匹配的序列化器
        // Two-phase lookups; local non-shared cache, then shared:
        JsonSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
        if (ser != null) {
            return ser;
        }
      // 没有,则去_serializerCache缓存中找
        // If not, maybe shared map already has it?
        ser = _serializerCache.typedValueSerializer(valueType);
        if (ser != null) {
            return ser;
        }
      // 还没有,则取组成一个
        // Well, let's just compose from pieces:
        ser = findValueSerializer(valueType, property);
...
        return ser;
    }

构成序列化器

public JsonSerializer<Object> findValueSerializer(Class<?> valueType, BeanProperty property)
        throws JsonMappingException
    {
      。。。这里只是在从缓存等查找了一遍
// 如果没有,则创建一个,而且放到缓存中 
                    // If neither, must create
                    ser = _createAndCacheUntypedSerializer(valueType);
                    // Not found? Must use the unknown type serializer, which will report error later on
                    if (ser == null) {
                        ser = getUnknownTypeSerializer(valueType);
                        // Should this be added to lookups?
                        if (CACHE_UNKNOWN_MAPPINGS) {
                            _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this);
                        }
                        return ser;
                    }
                }
            }
        }
        // at this point, resolution has occured, but not contextualization
        return (JsonSerializer<Object>) handleSecondaryContextualization(ser, property);
    }
protected JsonSerializer<Object> _createUntypedSerializer(JavaType type)
        throws JsonMappingException
    {
    
        synchronized (_serializerCache) {
         // 通过序列化工厂类,这个序列化工厂类是BeanSerializerFactory构建序列化器
            return (JsonSerializer<Object>)_serializerFactory.createSerializer(this, type);
        }
    }
public JsonSerializer<Object> createSerializer(SerializerProvider prov,
            JavaType origType)
        throws JsonMappingException
    {
        // Very first thing, let's check if there is explicit serializer annotation:
        final SerializationConfig config = prov.getConfig();
        BeanDescription beanDesc = config.introspect(origType);
// 先看有没有注解,注明了序列化器,自然是没有
        JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
        if (ser != null) {
            return (JsonSerializer<Object>) ser;
        }
        boolean staticTyping;
// 是否有注解,著名修改了这个是什么类,自然没有
        // Next: we may have annotations that further define types to use...
        JavaType type = modifyTypeByAnnotation(config, beanDesc.getClassInfo(), origType);
        if (type == origType) { // no changes, won't force static typing
            staticTyping = false;
        } else { // changes; assume static typing; plus, need to re-introspect if class differs
            staticTyping = true;
            if (!type.hasRawClass(origType.getRawClass())) {
                beanDesc = config.introspect(type);
            }
        }
      //找转换器,这里找不到   
     // Slight detour: do we have a Converter to consider?
        Converter<Object,Object> conv = beanDesc.findSerializationConverter();
        if (conv == null) { // no, simple
          // 真正构成序列化器的是这里
            return (JsonSerializer<Object>) _createSerializer2(prov, type, beanDesc, staticTyping);
        }

BeanSerializerFactory的_createSerializer2方法,构建序列化器

。。。这里一系列的各种找
 // Modules may provide serializers of POJO types:
// BeanSerializerFactory里自定义的序列器中找
            for (Serializers serializers : customSerializers()) {
                ser = serializers.findSerializer(config, type, beanDesc);
                if (ser != null) {
                    break;
                }
            }
。。。
最后找不到,就会构建一个BeanSerializer, 这个BeanSerializer里面包含了我传入的bean的属性,然后针对属性,又会构造BeanPropertyWriter ,这个BeanPropertyWriter 包含了针对属性的序列化器等,是一个只针对传入bena的一个很自定义的东西
                    ser = findBeanSerializer(prov, type, beanDesc);

// 构建了BeanSerializer之后,就是调用它的serialize序列化方法了
            ser.serialize(value, gen, this);

BeanSerializer的serialize方法

 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;
        }
// 写入"{"这个json开始字符,里面是char[]数组存储字符的
        gen.writeStartObject();
        // [databind#631]: Assign current value, to be accessible by custom serializers
        gen.setCurrentValue(bean);
        if (_propertyFilterId != null) {
            serializeFieldsFiltered(bean, gen, provider);
        } else {
// 然后序列化各个属性
            serializeFields(bean, gen, provider);
        }
        gen.writeEndObject();
    }

BeanSerializer父类BeanSerializerBase的方法

protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
        throws IOException, JsonGenerationException
    {
       ...
        try {
//遍历各个属性,然后分别序列化属性,如果属性是常用类型,自然就序列化器会直接序列化,如果是pojo,则属性序列化器是BeanSerializer,又会递归序列化
            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);
                }
            }
...
    }

到此:大概的整个序列化流程就是这样,当然,里面有很多细节,可以遇到具体问题具体来细节分析某一个模块。

前面的代码

        ObjectMapper mapper = new ObjectMapper();
        Person person = new Person();
        person.setName("Tom");
        person.setAge(40);
        person.setNow(LocalDateTime.now());
 String jsonString = mapper.writeValueAsString(person);
        System.out.println(jsonString);

输出是:

{"name":"Tom","age":40,"now":{"month":"JUNE","year":2020,"dayOfMonth":10,"hour":11,"minute":3,"monthValue":6,"nano":432000000,"second":30,"dayOfWeek":"WEDNESDAY","dayOfYear":162,"chronology":{"id":"ISO","calendarType":"iso8601"}}}

为什么LocalDateTime属性会被序列化成这样呢,原因是,LocalDateTime没有找到属于它的序列化器,使用的是BeanSerializer,所以,会把LocalDateTime的所有的成员递归序列,自然就序列成这样了。

代码大概分析完了。
然后,来看几个问题。
既然,LocalDateTime被档成bean序列化了,那么自然就考虑如果序列化成:xxxx-xx-xx xx:xx:xx的格式了。
注册JavaTimeModule

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        mapper.registerModule(javaTimeModule);

首先可以看到,JavaTimeModule的构造方法里,就已经添加了各种时间的序列化、反序列化器。保存在变量_deserializers中。

 public JavaTimeModule()
    {
        super(PackageVersion.VERSION);

        // First deserializers

        // // Instant variants:
        addDeserializer(Instant.class, InstantDeserializer.INSTANT);
        addDeserializer(OffsetDateTime.class, InstantDeserializer.OFFSET_DATE_TIME);
        addDeserializer(ZonedDateTime.class, InstantDeserializer.ZONED_DATE_TIME);

        // // Other deserializers
        addDeserializer(Duration.class, DurationDeserializer.INSTANCE);
        addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
        addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
        addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE);
        addDeserializer(MonthDay.class, JSR310StringParsableDeserializer.MONTH_DAY);
        addDeserializer(OffsetTime.class, OffsetTimeDeserializer.INSTANCE);
        addDeserializer(Period.class, JSR310StringParsableDeserializer.PERIOD);
        addDeserializer(Year.class, YearDeserializer.INSTANCE);
        addDeserializer(YearMonth.class, YearMonthDeserializer.INSTANCE);
        addDeserializer(ZoneId.class, JSR310StringParsableDeserializer.ZONE_ID);
        addDeserializer(ZoneOffset.class, JSR310StringParsableDeserializer.ZONE_OFFSET);
。。。
    public ObjectMapper registerModule(Module module){
     。。。
  module.setupModule(new Module.SetupContext()
        {
            // // // Accessors
。。。
            
            @Override
            public void addDeserializers(Deserializers d) {
                DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalDeserializers(d);
                mapper._deserializationContext = mapper._deserializationContext.with(df);
            }

            @Override
            public void addKeyDeserializers(KeyDeserializers d) {
                DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalKeyDeserializers(d);
                mapper._deserializationContext = mapper._deserializationContext.with(df);
            }

            @Override
            public void addSerializers(Serializers s) {
                mapper._serializerFactory = mapper._serializerFactory.withAdditionalSerializers(s);
            }

            @Override
            public void addKeySerializers(Serializers s) {
                mapper._serializerFactory = mapper._serializerFactory.withAdditionalKeySerializers(s);
            }
...
        });
        return this;
    }

}
 @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);

可以看到,最终会回调Module.SetupContext的add*等方法,也就是,mapper._serializerFactory.withAdditionalSerializers()方法。最终,javaTimeModile里面的所有序列化器,都添加到序列化工厂类serializerFactory里面了。

 @Override
    public void setupModule(SetupContext context)
    {
        if (_serializers != null) {
            context.addSerializers(_serializers);
        }
        if (_deserializers != null) {
            context.addDeserializers(_deserializers);
        }
        if (_keySerializers != null) {
            context.addKeySerializers(_keySerializers);
        }
        if (_keyDeserializers != null) {
            context.addKeyDeserializers(_keyDeserializers);
        }
        if (_abstractTypes != null) {
            context.addAbstractTypeResolver(_abstractTypes);

然后回过头来看找寻序列化器的代码,可以看到,会遍历_factoryConfig之前添加的所有序列化类,然后找寻序列化器,自然,localDateTime会在这里找到javaTimeModule里添加的序列化器。

for (Serializers serializers : customSerializers()) {
               ser = serializers.findSerializer(config, type, beanDesc);
               if (ser != null) {
                   break;
               }
           }
@Override
   protected Iterable<Serializers> customSerializers() {
       return _factoryConfig.serializers();
   }
   

至此,也就分析完了注册javaTimeModule解决localDateTime序列化问题的代码。

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        mapper.registerModule(javaTimeModule);

但是,如下代码,序列化后的结果是:
也就是说localDateTime被序列化成数组了。

{"name":"Tom","age":40,"now":[2020,6,10,14,30,37,527000000]}

为什么呢?查看JavaTimeModule里面添加的LocalDateTimeSerializer的序列化方法。

 @Override
    public void serialize(LocalDateTime dateTime, JsonGenerator generator, SerializerProvider provider)
            throws IOException
    {
// 当配置文件里使用了时间搓,默认会进入这里
        if (useTimestamp(provider)) {
// 这里通过方法名就可以看到,这里是写入数组,也就是默认这个序列化器就是会把localDateTime序列化成数组的
            generator.writeStartArray();
            generator.writeNumber(dateTime.getYear());
            generator.writeNumber(dateTime.getMonthValue());
            generator.writeNumber(dateTime.getDayOfMonth());
            generator.writeNumber(dateTime.getHour());
            generator.writeNumber(dateTime.getMinute());
            if(dateTime.getSecond() > 0 || dateTime.getNano() > 0)
            {
                generator.writeNumber(dateTime.getSecond());
                if(dateTime.getNano() > 0)
                {
                    if(provider.isEnabled(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS))
                        generator.writeNumber(dateTime.getNano());
                    else
                        generator.writeNumber(dateTime.get(ChronoField.MILLI_OF_SECOND));
                }
            }
            generator.writeEndArray();
        } else {
// 如果配置禁用了时间戳,则走入这里,那么会使用事先指定的_formatter格式化,默认new LocalDateTimeSerializer没有指定。
            String str = (_formatter == null) ? dateTime.toString() : dateTime.format(_formatter);
            generator.writeString(str);
        }
    }

可以看一下,如果我们禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)属性,就会使用指定了的格式化器格式化。

protected boolean useTimestamp(SerializerProvider provider) {
        if (_useTimestamp != null) {
            return _useTimestamp.booleanValue();
        }
        // assume that explicit formatter definition implies use of textual format
        if (_formatter != null) { 
            return false;
        }
        return provider.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }

禁用掉。

输出,默认格式未ISO类型的。因为默认没有指定formatter,所以调用的是LocalDateTime的tostring方法,所以会序列化成iso类型。

{"name":"Tom","age":40,"now":"2020-06-10T14:39:41.615"}

那么我们可以指定formatter吗?可以看到,LocalDateTimeSerializer的所有的构造方法都是protected,只提供一个INSTANCE 使用,所以,想通过自带的LocalDateTimeSerializer指定formatter是不可能的。

public class LocalDateTimeSerializer extends JSR310FormattedSerializerBase<LocalDateTime>
{
    private static final long serialVersionUID = 1L;

    public static final LocalDateTimeSerializer INSTANCE = new LocalDateTimeSerializer();

    protected LocalDateTimeSerializer() {
        super(LocalDateTime.class);
    }

    private LocalDateTimeSerializer(LocalDateTimeSerializer base,
            Boolean useTimestamp, DateTimeFormatter dtf) {
        super(base, useTimestamp, dtf);
    }

只能自己定义LocalDateTimeSerializer,把它们的LocalDateTimeSerializer照抄,然后改一下dateTimeFormatter。

public class LocalDateTimeSerializer  extends JsonSerializer<LocalDateTime> {

    private DateTimeFormatter dateTimeFormatter;

    public LocalDateTimeSerializer(DateTimeFormatter dateTimeFormatter){
        this.dateTimeFormatter = dateTimeFormatter;
    }
    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException {
        String str = (dateTimeFormatter == null) ? localDateTime.toString() : localDateTime.format(dateTimeFormatter);
        generator.writeString(str);
    }
}

然后添加到javaTimeModule中,因为javaTimeModule里使用的是hash映射,所以add相同的LocalDateTime.class,会覆盖原有的。

 LocalDateTimeSerializer LocalDateTimeSerializer = new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        javaTimeModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer);

至此, 终于序列化好了LocalDateTime

{"name":"Tom","age":40,"now":"2020-06-10 14:48:04"}

其实,低版本的jackson才需要自己定义序列化器,高版本不需要,比如2.9.9版本,就可以直接指定formater。

        javaTimeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
上一篇下一篇

猜你喜欢

热点阅读