ObjectMapper中MapperFeature.USE_GETTERS_AS_SETTERS对反序列化的影响

一、依赖版本

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 

针对文章内容自己加了一些注释

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值