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

来源:互联网 发布:caffe 继续训练 编辑:程序博客网 时间:2024/06/07 12:14

从一个问题开始

假设有这么一个类:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj(int x) {  
  6.         this.x = x;  
  7.     }  
  8.       
  9. }  
和下面的测试代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Test  
  2. public void gson() {  
  3.     MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);  
  4.     Assert.assertEquals(1, obj.x);  
  5. }  
那么GSON是通过什么样的方式创建MyObj对象的呢?答案可能会出乎你的意料(至少出乎了我的意料)。


InstanceCreator和ObjectConstructor

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Defines a generic object construction factory.  The purpose of this class 
  3.  * is to construct a default instance of a class that can be used for object 
  4.  * navigation while deserialization from its JSON representation. 
  5.  * 
  6.  * @author Inderjeet Singh 
  7.  * @author Joel Leitch 
  8.  */  
  9. public interface ObjectConstructor<T> {  
  10.   
  11.   /** 
  12.    * Returns a new instance. 
  13.    */  
  14.   public T construct();  
  15. }  
那么ObjectConstructor从何而来呢?答案在ConstructorConstructor里:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {  
  2.   final Type type = typeToken.getType();  
  3.   final Class<? super T> rawType = typeToken.getRawType();  
  4.   
  5.   // first try an instance creator  
  6.   
  7.   @SuppressWarnings("unchecked"// types must agree  
  8.   final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);  
  9.   if (typeCreator != null) {  
  10.     return new ObjectConstructor<T>() {  
  11.       public T construct() {  
  12.         return typeCreator.createInstance(type);  
  13.       }  
  14.     };  
  15.   }  
  16.   
  17.   // Next try raw type match for instance creators  
  18.   @SuppressWarnings("unchecked"// types must agree  
  19.   final InstanceCreator<T> rawTypeCreator =  
  20.       (InstanceCreator<T>) instanceCreators.get(rawType);  
  21.   if (rawTypeCreator != null) {  
  22.     return new ObjectConstructor<T>() {  
  23.       public T construct() {  
  24.         return rawTypeCreator.createInstance(type);  
  25.       }  
  26.     };  
  27.   }  
  28.   
  29.   ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);  
  30.   if (defaultConstructor != null) {  
  31.     return defaultConstructor;  
  32.   }  
  33.   
  34.   ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);  
  35.   if (defaultImplementation != null) {  
  36.     return defaultImplementation;  
  37.   }  
  38.   
  39.   // finally try unsafe  
  40.   return newUnsafeAllocator(type, rawType);  
  41. }  

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

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

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


有默认构造函数的情况

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj() {  
  6.         throw new RuntimeException("!!!"); // <---  
  7.     }  
  8.       
  9. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Test(expected = RuntimeException.class// <---  
  2. public void gson() {  
  3.     new Gson().fromJson("{\"x\":1}", MyObj.class);  
  4. }  
测试通过!


没有默认构造函数的情况

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj(int x) { // <---  
  6.         throw new RuntimeException("!!!");  
  7.     }  
  8.       
  9. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Test  
  2. public void gson() {  
  3.     MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);  
  4.     Assert.assertEquals(1, obj.x);  
  5. }  
测试通过!

UnsafeAllocator

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Do sneaky things to allocate objects without invoking their constructors. 
  3.  * 
  4.  * @author Joel Leitch 
  5.  * @author Jesse Wilson 
  6.  */  
  7. public abstract class UnsafeAllocator {  
  8.   public abstract <T> T newInstance(Class<T> c) throws Exception;  
  9.   
  10.   public static UnsafeAllocator create() {  
  11.     // try JVM  
  12.     // public class Unsafe {  
  13.     //   public Object allocateInstance(Class<?> type);  
  14.     // }  
  15.     try {  
  16.       Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");  
  17.       Field f = unsafeClass.getDeclaredField("theUnsafe");  
  18.       f.setAccessible(true);  
  19.       final Object unsafe = f.get(null);  
  20.       final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);  
  21.       return new UnsafeAllocator() {  
  22.         @Override  
  23.         @SuppressWarnings("unchecked")  
  24.         public <T> T newInstance(Class<T> c) throws Exception {  
  25.           return (T) allocateInstance.invoke(unsafe, c);  
  26.         }  
  27.       };  
  28.     } catch (Exception ignored) {  
  29.     }  
  30.   
  31.     ...  
  32.       
  33.     // give up  
  34.     return new UnsafeAllocator() {  
  35.       @Override  
  36.       public <T> T newInstance(Class<T> c) {  
  37.         throw new UnsupportedOperationException("Cannot allocate " + c);  
  38.       }  
  39.     };  
  40.   }  
  41. }  

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

[java] view plain copy
  1. public abstract class UnsafeAllocator {    
  2.   public abstract <T> T newInstance(Class<T> c) throws Exception;    
  3.     
  4.   public static UnsafeAllocator create() {  
  5.       return new UnsafeAllocator() {    
  6.         @Override    
  7.         @SuppressWarnings("unchecked")    
  8.         public <T> T newInstance(Class<T> c) throws Exception {    
  9.           Unsafe unsafe = sun.misc.Unsafe.theUnsafe; // <--  
  10.           return (T) unsafe.allocateInstance(c); // <--  
  11.         }    
  12.       };   
  13.   }  
  14. }  

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

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj(int x) { // <---  
  6.         this.x = x;  
  7.     }  
  8.       
  9. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Test  
  2. public void setFinal() throws Exception {  
  3.     MyObj obj = new MyObj(1);  
  4.     Assert.assertEquals(1, obj.x);  
  5.       
  6.     Field f = obj.getClass().getField("x");  
  7.     f.setAccessible(true); // <---  
  8.     f.set(obj, 2);  
  9.     Assert.assertEquals(2, obj.x);  
  10. }  
测试通过!

结论

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

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

0 0