您的位置:首页 > 其它

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

2016-09-16 17:14 609 查看

从一个问题开始

假设有这么一个类:

[java] view
plain copy

 





class MyObj {  

      

    public final int x;  

      

    public MyObj(int x) {  

        this.x = x;  

    }  

      

}  

和下面的测试代码:

[java] view
plain copy

 





@Test  

public void gson() {  

    MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);  

    Assert.assertEquals(1, obj.x);  

}  

那么GSON是通过什么样的方式创建MyObj对象的呢?答案可能会出乎你的意料(至少出乎了我的意料)。

InstanceCreator和ObjectConstructor

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

[java] view
plain copy

 





/** 

 * 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里:

[java] view
plain copy

 





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);  

}  

代码看起来很复杂,但实际上井然有序:
如果我们(通过GsonBuilder)注册过InstanceCreator,则交给InstanceCreator来创建实例
如果类有默认构造函数,则通过反射调用默认构造函数创建实例
如果想要创建ListMap等接口的实例,则走这里
否则交给神秘的UnsafeAllocator来收场

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

有默认构造函数的情况

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

[java] view
plain copy

 





class MyObj {  

      

    public final int x;  

      

    public MyObj() {  

        throw new RuntimeException("!!!"); // <---  

    }  

      

}  

[java] view
plain copy

 





@Test(expected = RuntimeException.class) // <---  

public void gson() {  

    new Gson().fromJson("{\"x\":1}", MyObj.class);  

}  

测试通过!

没有默认构造函数的情况

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

[java] view
plain copy

 





class MyObj {  

      

    public final int x;  

      

    public MyObj(int x) { // <---  

        throw new RuntimeException("!!!");  

    }  

      

}  

[java] view
plain copy

 





@Test  

public void gson() {  

    MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);  

    Assert.assertEquals(1, obj.x);  

}  

测试通过!

UnsafeAllocator

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

[java] view
plain copy

 





/** 

 * 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);  

      }  

    };  

  }  

}  

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

[java] view
plain copy

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字段是怎么处理的?

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

[java] view
plain copy

 





class MyObj {  

      

    public final int x;  

      

    public MyObj(int x) { // <---  

        this.x = x;  

    }  

      

}  

[java] view
plain copy

 





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

这种做法通常是很危险的,所以非专业人士请勿效仿
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: