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

从一个问题开始

假设有这么一个类:

    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字段赋值。

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


你可能感兴趣的:(GSON使用笔记(2) -- 反序列化时GSON如何创建对象实例)