日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

GSON使用筆記(2)

 王慶峰 2015-06-19

從一個(gè)問題開始

假設(shè)有這么一個(gè)類:

  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj(int x) {  
  6.         this.x = x;  
  7.     }  
  8.       
  9. }  
和下面的測試代碼:
  1. @Test  
  2. public void gson() {  
  3.     MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);  
  4.     Assert.assertEquals(1, obj.x);  
  5. }  
那么GSON是通過什么樣的方式創(chuàng)建MyObj對象的呢?答案可能會出乎你的意料(至少出乎了我的意料)。


InstanceCreator和ObjectConstructor

經(jīng)過斷點(diǎn)調(diào)試或者閱讀源代碼不難發(fā)現(xiàn),GSON是使用ObjectConstructor來創(chuàng)建對象實(shí)例的,這點(diǎn)從代碼注釋里也能看的出來:

  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里:
  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. }  

代碼看起來很復(fù)雜,但實(shí)際上井然有序:

  1. 如果我們(通過GsonBuilder)注冊過InstanceCreator,則交給InstanceCreator來創(chuàng)建實(shí)例
  2. 如果類有默認(rèn)構(gòu)造函數(shù),則通過反射調(diào)用默認(rèn)構(gòu)造函數(shù)創(chuàng)建實(shí)例
  3. 如果想要創(chuàng)建ListMap等接口的實(shí)例,則走這里
  4. 否則交給神秘的UnsafeAllocator來收場

第一和第三種情況暫不考慮,下面來分析第二和第四種情況。


有默認(rèn)構(gòu)造函數(shù)的情況

按照前面的分析,這種情況GSON是通過調(diào)用默認(rèn)構(gòu)造函數(shù)來創(chuàng)建對象實(shí)例的,讓我們證明這一點(diǎn):

  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj() {  
  6.         throw new RuntimeException("!!!"); // <---  
  7.     }  
  8.       
  9. }  
  1. @Test(expected = RuntimeException.class// <---  
  2. public void gson() {  
  3.     new Gson().fromJson("{\"x\":1}", MyObj.class);  
  4. }  
測試通過!


沒有默認(rèn)構(gòu)造函數(shù)的情況

還是通過代碼來證明這一點(diǎn):

  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj(int x) { // <---  
  6.         throw new RuntimeException("!!!");  
  7.     }  
  8.       
  9. }  
  1. @Test  
  2. public void gson() {  
  3.     MyObj obj = new Gson().fromJson("{\"x\":1}", MyObj.class);  
  4.     Assert.assertEquals(1, obj.x);  
  5. }  
測試通過!

UnsafeAllocator

現(xiàn)在讓我們一睹UnsafeAllocator的風(fēng)采:

  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. }  

去掉反射后,代碼看起來大概是這樣:

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

答案是,通過反射。詳細(xì)情況可以參考這個(gè)問題,下面我們僅通過代碼來證明這一點(diǎn):

  1. class MyObj {  
  2.       
  3.     public final int x;  
  4.       
  5.     public MyObj(int x) { // <---  
  6.         this.x = x;  
  7.     }  
  8.       
  9. }  
  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. }  
測試通過!

結(jié)論

反序列化時(shí),如果一個(gè)類沒有默認(rèn)構(gòu)造函數(shù),那么GSON是通過JDK內(nèi)部API來創(chuàng)建對象實(shí)例的,并且通過反射final字段賦值。

這種做法通常是很危險(xiǎn)的,所以非專業(yè)人士請勿效仿


    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多