GSON使用笔记(2) -- 反序列化时GSON如何创建对象实例

本文深入探讨了GSON在反序列化过程中如何创建对象实例,特别是针对有无默认构造函数的情况。当类有默认构造函数时,GSON通过反射调用构造函数创建实例;若无默认构造函数,GSON则使用UnsafeAllocator。对于final字段,GSON通过反射进行处理。警告:这种方式可能涉及风险,非专业人士应谨慎操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从一个问题开始

假设有这么一个类:

    class MyObj {
        
        public final int x;
        
        public MyObj(int x) {
            this.x = x;
        }
        
    }
和下面的测试代码:

    @Test
    public void gson() {
        MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);
        Assert.assertEquals(1, obj.x);
    }
那么GSON是通过什么样的方式创建 MyObj对象的呢?答案可能会出乎你的意料(至少出乎了我的意料)。


InstanceCreator和ObjectConstructor

经过断点调试或者阅读源代码不难发现,GSON是使用ObjectConstructor来创建对象实例的,这点从代码注释里也能看的出来:

/**
 * Defines a generic object construction factory.  The purpose of this class
 * is to construct a default instance of a class that can be used for object
 * navigation while deserialization from its JSON representation.
 *
 * @author Inderjeet Singh
 * @author Joel Leitch
 */
public interface ObjectConstructor<T> {

  /**
   * Returns a new instance.
   */
  public T construct();
}
那么ObjectConstructor从何而来呢?答案在 ConstructorConstructor里:

  public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
    final Type type = typeToken.getType();
    final Class<? super T> rawType = typeToken.getRawType();

    // first try an instance creator

    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
    if (typeCreator != null) {
      return new ObjectConstructor<T>() {
        public T construct() {
          return typeCreator.createInstance(type);
        }
      };
    }

    // Next try raw type match for instance creators
    @SuppressWarnings("unchecked") // types must agree
    final InstanceCreator<T> rawTypeCreator =
        (InstanceCreator<T>) instanceCreators.get(rawType);
    if (rawTypeCreator != null) {
      return new ObjectConstructor<T>() {
        public T construct() {
          return rawTypeCreator.createInstance(type);
        }
      };
    }

    ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
    if (defaultConstructor != null) {
      return defaultConstructor;
    }

    ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
    if (defaultImplementation != null) {
      return defaultImplementation;
    }

    // finally try unsafe
    return newUnsafeAllocator(type, rawType);
  }

代码看起来很复杂,但实际上井然有序:

  1. 如果我们(通过GsonBuilder)注册过InstanceCreator,则交给InstanceCreator来创建实例
  2. 如果类有默认构造函数,则通过反射调用默认构造函数创建实例
  3. 如果想要创建ListMap等接口的实例,则走这里
  4. 否则交给神秘的UnsafeAllocator来收场

第一和第三种情况暂不考虑,下面来分析第二和第四种情况。


有默认构造函数的情况

按照前面的分析,这种情况GSON是通过调用默认构造函数来创建对象实例的,让我们证明这一点:

    class MyObj {
        
        public final int x;
        
        public MyObj() {
            throw new RuntimeException("!!!"); // <---
        }
        
    }
    @Test(expected = RuntimeException.class) // <---
    public void gson() {
        new Gson().fromJson("{\"x\":1}", MyObj.class);
    }
测试通过!


没有默认构造函数的情况

还是通过代码来证明这一点:

    class MyObj {
        
        public final int x;
        
        public MyObj(int x) { // <---
            throw new RuntimeException("!!!");
        }
        
    }
    @Test
    public void gson() {
        MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);
        Assert.assertEquals(1, obj.x);
    }
测试通过!

UnsafeAllocator

现在让我们一睹UnsafeAllocator的风采:

/**
 * Do sneaky things to allocate objects without invoking their constructors.
 *
 * @author Joel Leitch
 * @author Jesse Wilson
 */
public abstract class UnsafeAllocator {
  public abstract <T> T newInstance(Class<T> c) throws Exception;

  public static UnsafeAllocator create() {
    // try JVM
    // public class Unsafe {
    //   public Object allocateInstance(Class<?> type);
    // }
    try {
      Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
      Field f = unsafeClass.getDeclaredField("theUnsafe");
      f.setAccessible(true);
      final Object unsafe = f.get(null);
      final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
      return new UnsafeAllocator() {
        @Override
        @SuppressWarnings("unchecked")
        public <T> T newInstance(Class<T> c) throws Exception {
          return (T) allocateInstance.invoke(unsafe, c);
        }
      };
    } catch (Exception ignored) {
    }

    ...
    
    // give up
    return new UnsafeAllocator() {
      @Override
      public <T> T newInstance(Class<T> c) {
        throw new UnsupportedOperationException("Cannot allocate " + c);
      }
    };
  }
}

去掉反射后,代码看起来大概是这样:

public abstract class UnsafeAllocator {  
  public abstract <T> T newInstance(Class<T> c) throws Exception;  
  
  public static UnsafeAllocator create() {
      return new UnsafeAllocator() {  
        @Override  
        @SuppressWarnings("unchecked")  
        public <T> T newInstance(Class<T> c) throws Exception {  
          Unsafe unsafe = sun.misc.Unsafe.theUnsafe; // <--
          return (T) unsafe.allocateInstance(c); // <--
        }  
      }; 
  }
}

那么final字段是怎么处理的?

答案是,通过反射。详细情况可以参考这个问题,下面我们仅通过代码来证明这一点:

    class MyObj {
        
        public final int x;
        
        public MyObj(int x) { // <---
            this.x = x;
        }
        
    }
    @Test
    public void setFinal() throws Exception {
        MyObj obj = new MyObj(1);
        Assert.assertEquals(1, obj.x);
        
        Field f = obj.getClass().getField("x");
        f.setAccessible(true); // <---
        f.set(obj, 2);
        Assert.assertEquals(2, obj.x);
    }
测试通过!

结论

反序列化时,如果一个类没有默认构造函数,那么GSON是通过JDK内部API来创建对象实例的,并且通过反射final字段赋值。

这种做法通常是很危险的,所以非专业人士请勿效仿


包含以下java源文件: com.google.gson.DefaultDateTypeAdapter.class com.google.gson.ExclusionStrategy.class com.google.gson.FieldAttributes.class com.google.gson.FieldNamingPolicy.class com.google.gson.FieldNamingStrategy.class com.google.gson.Gson.class com.google.gson.GsonBuilder.class com.google.gson.InstanceCreator.class com.google.gson.JsonArray.class com.google.gson.JsonDeserializationContext.class com.google.gson.JsonDeserializer.class com.google.gson.JsonElement.class com.google.gson.JsonIOException.class com.google.gson.JsonNull.class com.google.gson.JsonObject.class com.google.gson.JsonParseException.class com.google.gson.JsonParser.class com.google.gson.JsonPrimitive.class com.google.gson.JsonSerializationContext.class com.google.gson.JsonSerializer.class com.google.gson.JsonStreamParser.class com.google.gson.JsonSyntaxException.class com.google.gson.LongSerializationPolicy.class com.google.gson.TreeTypeAdapter.class com.google.gson.TypeAdapter.class com.google.gson.TypeAdapterFactory.class com.google.gson.annotations.Expose.class com.google.gson.annotations.SerializedName.class com.google.gson.annotations.Since.class com.google.gson.annotations.Until.class com.google.gson.internal.ConstructorConstructor.class com.google.gson.internal.Excluder.class com.google.gson.internal.JsonReaderInternalAccess.class com.google.gson.internal.LazilyParsedNumber.class com.google.gson.internal.LinkedTreeMap.class com.google.gson.internal.ObjectConstructor.class com.google.gson.internal.Primitives.class com.google.gson.internal.Streams.class com.google.gson.internal.UnsafeAllocator.class com.google.gson.internal.bind.ArrayTypeAdapter.class com.google.gson.internal.bind.CollectionTypeAdapterFactory.class com.google.gson.internal.bind.DateTypeAdapter.class com.google.gson.internal.bind.JsonTreeReader.class com.google.gson.internal.bind.JsonTreeWriter.class com.google.gson.internal.bind.MapTypeAdapterFactory.class com.google.gson.internal.bind.ObjectTypeAdapter.class com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.class com.google.gson.internal.bind.SqlDateTypeAdapter.class com.google.gson.internal.bind.TimeTypeAdapter.class com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.class com.google.gson.internal.bind.TypeAdapters.class com.google.gson.reflect.TypeToken.class com.google.gson.stream.JsonReader.class com.google.gson.stream.JsonScope.class com.google.gson.stream.JsonToken.class com.google.gson.stream.JsonWriter.class com.google.gson.stream.MalformedJsonException.class
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值