一、依赖版本
jackson-databind-2.8.10
二、MapperFeature.USE_GETTERS_AS_SETTERS的作用
源码解释
/**
* Feature that determines whether otherwise regular "getter"
* methods (but only ones that handle Collections and Maps,
* not getters of other type)
* can be used for purpose of getting a reference to a Collection
* and Map to modify the property, without requiring a setter
* method.
* This is similar to how JAXB framework sets Collections and
* Maps: no setter is involved, just setter.
*<p>
* Note that such getters-as-setters methods have lower
* precedence than setters, so they are only used if no
* setter is found for the Map/Collection property.
*<p>
* Feature is enabled by default.
*/
USE_GETTERS_AS_SETTERS(true)
USE_GETTERS_AS_SETTERS开启时,针对Map和Collection类型的属性可以用getter防获取属性的引用来改变属性的值,不需要setter方法。
反序列化:在目标类中,用我们期望的值,修改那些可以被修改的属性值。正常的属性都是通过setter进行属性赋值,而Collection和Map可以直接用getter获取到属性的引用以此直接修改属性值,所以当USE_GETTERS_AS_SETTERS开启时,ObjectMapper认为Map和Collection这两种值,也应当被反序列化。
三、示例
创建一个用户类。
只有一个getAddresses方法,并没有addresses属性
package deserialize;
import java.util.ArrayList;
import java.util.List;
public class User {
private Long id;
private String name;
public List<String> getAddresses() {
List<String> list = new ArrayList<>();
list.add("asd");
return list;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
序列化时,ObjectMapper会将addresses作为属性加到为JSON里(如果没有addresses属性,我认为完全可以不序列化)。
- 开启MapperFeature.USE_GETTERS_AS_SETTERS(默认开启)进行测试, 代码如下:
@Test
public void serialize() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
// 关闭USE_GETTERS_AS_SETTERS 会报错找不到属性,正常属性用
// com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet赋值
// 开启时找到属性执行方法就结束,会调用com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet
// 该方法执行完结束
// objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS);
User user = new User();
user.setId(1L);
user.setName("zhang");
String s = objectMapper.writeValueAsString(user);
System.out.println(s);
Object value = objectMapper.readValue(s, User.class);
System.out.println(value);
}
运行结果:
此时,调用ObjectMapper.readValue进行反序列化,正常。
- 关闭MapperFeature.USE_GETTERS_AS_SETTERS(默认开启)进行测试, 代码如下:
@Test
public void serialize() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
// 关闭USE_GETTERS_AS_SETTERS 会报错找不到属性,正常属性用
// com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet赋值
// 开启时找到属性执行方法就结束,会调用com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet
// 该方法执行完结束
objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS);
User user = new User();
user.setId(1L);
user.setName("zhang");
String s = objectMapper.writeValueAsString(user);
System.out.println(s);
Object value = objectMapper.readValue(s, User.class);
System.out.println(value);
}
运行结果:
此时,调用ObjectMapper.readValue进行反序列化,抛出异常,无法识别addresses属性。
四、反序列化ObjectMapper.readValue源码分析
1、反序列化过程
com.fasterxml.jackson.databind.ObjectMapper#readValue(java.lang.String, java.lang.Class<T>)
com.fasterxml.jackson.databind.ObjectMapper#_readMapAndClose
// 获取反序列化器
com.fasterxml.jackson.databind.ObjectMapper#_findRootDeserializer
// 进行反序列化
com.fasterxml.jackson.databind.deser.BeanDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
// 反序列化
com.fasterxml.jackson.databind.deser.BeanDeserializer#vanillaDeserialize
private final Object vanillaDeserialize(JsonParser p,
DeserializationContext ctxt, JsonToken t)
throws IOException
{
final Object bean = _valueInstantiator.createUsingDefault(ctxt);
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
String propName = p.getCurrentName();
do {
p.nextToken();
// MapperFeature.USE_GETTERS_AS_SETTERS 开启时(默认开启), 执行该方法会返回SetterlessProperty对象,无事发生
// MapperFeature.USE_GETTERS_AS_SETTERS 关闭时,该值为空 会抛出异常
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
// 反序列化中的_beanProperties存在该属性值,该属性值就会被赋值, 但是只有一个getter没有字段定义,则无事发生
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;
}
遍历目标类User中所有的属性名propName(因为存在getAddresses方法,jackson这里认为包含addresses属性)。如果在com.fasterxml.jackson.databind.deser.BeanDeserializerBase#_beanProperties中存在该属性则进行属性赋值,否则调用com.fasterxml.jackson.databind.deser.BeanDeserializerBase#handleUnknownVanilla方法抛出异常。
这里先放结论:
- MapperFeature.USE_GETTERS_AS_SETTERS开启,_beanProperties中存在addresses的属性,所以程序可以正常执行,但由于在User类中没有该属性,所以就有了上面的反序列化的结果(addresses并没有用)。
- 反之,_beanProperties中不存在addresses的属性,所以会走到handleUnknownVanilla抛出异常
_beanProperties是反序列化器com.fasterxml.jackson.databind.deser.BeanDeserializer的父类com.fasterxml.jackson.databind.deser.BeanDeserializerBase中的属性,下面看反序列化器,是如何创建的。
2、创建反序列化器
com.fasterxml.jackson.databind.ObjectMapper#readValue(java.lang.String, java.lang.Class<T>)
com.fasterxml.jackson.databind.ObjectMapper#_readMapAndClose
// 获取反序列化器
com.fasterxml.jackson.databind.ObjectMapper#_findRootDeserializer
com.fasterxml.jackson.databind.DeserializationContext#findRootValueDeserializer
com.fasterxml.jackson.databind.deser.DeserializerCache#findValueDeserializer
// 创建并缓存反序列化器
com.fasterxml.jackson.databind.deser.DeserializerCache#_createAndCacheValueDeserializer
com.fasterxml.jackson.databind.deser.DeserializerCache#_createAndCache2
// 创建反序列化器
com.fasterxml.jackson.databind.deser.DeserializerCache#_createDeserializer
com.fasterxml.jackson.databind.deser.DeserializerCache#_createDeserializer2
// 创建实例的反序列化器
com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#createBeanDeserializer
// 构建实例的反序列化器
com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#buildBeanDeserializer
// 添加实例的属性到序列化器中,这里添加的属性就是向上文的_beanProperties中赋值
com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#addBeanProps
在addBeanProps方法中有这样一段代码
// 遍历实例的属性定义 只要存在getter属性定义就会有值
// At which point we still have all kinds of properties; not all with mutators:
for (BeanPropertyDefinition propDef : propDefs) {
SettableBeanProperty prop = null;
/* 18-Oct-2013, tatu: Although constructor parameters have highest precedence,
* we need to do linkage (as per [databind#318]), and so need to start with
* other types, and only then create constructor parameter, if any.
*/
if (propDef.hasSetter()) {
JavaType propertyType = propDef.getSetter().getParameterType(0);
prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
} else if (propDef.hasField()) {
JavaType propertyType = propDef.getField().getType();
prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
} else if (useGettersAsSetters && propDef.hasGetter()) {
// 如果useGettersAsSetters开启 并且有getter则认为该属性需要被反序列化
/* May also need to consider getters
* for Map/Collection properties; but with lowest precedence
*/
AnnotatedMethod getter = propDef.getGetter();
// should only consider Collections and Maps, for now?
Class<?> rawPropertyType = getter.getRawType();
if (Collection.class.isAssignableFrom(rawPropertyType)
|| Map.class.isAssignableFrom(rawPropertyType)) {
// 构造SetterlessProperty对象
prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
}
}
遍历实例中的属性定义(因为存在getAddresses方法,所以这里有三个值id,name和addresses)。
如果该属性有setter方法或者有该属性,都会构造出一个com.fasterxml.jackson.databind.deser.SettableBeanProperty对象;
如果USE_GETTERS_AS_SETTERS开启,并且有getter方法,并且该属性的类型是Collection或者Map,会构造一个com.fasterxml.jackson.databind.deser.impl.SetterlessProperty对象,以上对象都用prop来保存。如果prop不为空则会将该属性加到反序列化器中的_beanProperties中,到反序列化时,程序会正常运行;当prop为空时,则不会加入到_beanProperties中,到反序列化时,会抛出无法识别属性的异常。
五、结论
对于存在getter方法,而没有对应属性值的类进行序列化时,会将getter方法对应的属性值一起序列化,转成json。
对于存在getter方法的并且getter方法的返回值是Map或者Collection类型的类,进行序列化后,反序列化时,如果开启USE_GETTERS_AS_SETTERS,则会正常运行;如果关闭USE_GETTERS_AS_SETTERS则会抛出异常。
如有错误,欢迎指正。
源码地址:https://gitee.com/zhang-dj/jackson-databind-jackson-databind-2.8.10
针对文章内容自己加了一些注释