jackson源码分析
背景:之前碰到序列化的问题,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")));