1. 程式人生 > >Gson的反射解析機制詳解(2)

Gson的反射解析機制詳解(2)

在上一篇部落格中籠統的介紹了Gson中解析json的整體流程,但是具體細節並沒有說明,本篇就簡單的梳理一下:
1)怎麼利用反射來建立自定義的JavaBean?
2)怎麼給JavaBen的變數類來賦值?
3)集合類物件怎麼建立?

通過閱讀Gson原始碼可得出以下的結論:
1)先 獲取type獲取JavaBean的java.lang.reflect.Constructor,構造器為預設構造器
2)通過Constructor的newInstance()來建立一個type型別的JavaBean物件。
3)迴圈遍歷json中的鍵值對,獲取key和value;並且獲取key對應的JavaBean中的Field
4)將value通過Filed的set(JavaBean,value)方法,將value賦值給Javaben對應的Field中去

例項程式碼如下:


        Constructor<?> c = Person.class.getDeclaredConstructor();
        Person  person=(Personn) c.newInstance();
        //獲取Person中的變數
        Field[] fields = Person.class.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //獲取變數名
String name = field.getName(); if (name.equals("name"){ field.set(person, "凌雲"); } else if (name.equals("sex")) { field.set(person, "屌絲男"); } else if(name.equals("job")){ field.set(person,"IT monkey"); } } return
person

上面的例子程式可以說是Gson中對json解析的簡單雛形,其實這些結論都是Gson對於Java反射知識的基本應用,只不過在用的時候結合泛型做了有效的封裝來進行通用。下面就簡單的梳理一下Gson是怎麼來進行處理的。
1)json串鍵值對key/value中key繫結JavaBean中對應的變數。(本節假設JavaBean中便令名沒有用到註解), 在Gson中建立了一個BoundField的類,該類就是負責json中key與JavaBean的變數名進行對映繫結:

   //
   private final Map<String, BoundField> boundFields;
    //該類在ReflectiveTypeAdapterFactory中定義
  static abstract class BoundField {
    //json的key或者JavaBean中的變數名
    final String name;
    ......
 protected BoundField(String name, boolean serialized, boolean deserialized) {
      this.name = name;
      this.serialized = serialized;
      this.deserialized = deserialized;
    }

在ReflectiveTypeAdapterFactory類中通過getBoundFields方法來對boundFields(詳見Gson的反射解析機制詳解(1)這個map進行初始化,主要是迴圈遍歷JavaBean中的變數,然後把每個變數封裝成一個BoundField,放入map中.該map的key對應的是JavaBean的變數名,value對應的就是BoundField。而BoundField還負責對其繫結的JavaBean變數或者json中key進行讀取解析,然後賦值給該JavaBean變數,通過Field的set(JavaBean,value)來完成(這部分詳見Gson的反射解析機制詳解(1) )。.方法基本的骨架跟文章開頭類似。

2)步驟1)中提到,最終是通過Field.setValue(JavaBean,value)來完成當前變數的賦值操作,方法引數中value我們知道是通過讀取json來獲取,而第一個引數JavaBean是怎麼建立的?答案是通過反射建立Gson 的TypeToken 中T代表的物件,也就是JavaBean物件。具體的就是用Constructor.newInstance();見文章開頭例項方法:但在Gson中是通過ConstructorConstructor這個類來完成這一工作的:該類提供了newDefaultConstructor來獲取JavaBean中的預設構造器,並通過該構造器建立一個JavaBean物件:

 private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {

      //此方法返回具有指定引數列表建構函式的建構函式物件,在這裡是獲取預設的構造器
      final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
      if (!constructor.isAccessible()) {//設定訪問許可權
        constructor.setAccessible(true);
      }
      return new ObjectConstructor<T>() {
        @SuppressWarnings("unchecked") // T is the same raw type as is requested
        public T construct() {

            Object[] args = null;
            //建立JavaBean的物件,並返回之
            return (T) constructor.newInstance(args);
          }
      };

  }

通過上面的程式碼發現該方法返回的是一個ObjectConstructor的匿名子類,通過呼叫該子類的construct()來建立並返回JavaBean物件。(ObjectConstructor是一個泛型介面,該介面就提供了一個construct()方法),返回的JavaBean物件交給Field的set方法第一個引數。
所以通過ReflectiveTypeAdapterFactory的create()方法返回的Adapter的read方法就是如下:

public T read(JsonReader in) throws IOException {
     //此處省略了程式碼

      //獲取通過步驟2建立的JavaBean物件
      T instance = constructor.construct();
      try {
        in.beginObject();
        while (in.hasNext()) {//遍歷當前JsonObject的值
          //獲取name,也就是json鍵值對的key
          String name = in.nextName();
          //獲取這個name 繫結的JavaBean變數名
          BoundField field = boundFields.get(name);
          if (field == null || !field.deserialized) {
            in.skipValue();
          } else {
            //讀取此時name對應的值,設定到instance物件中
            field.read(in, instance);
          }
        }
      } catch (IllegalStateException e) {
        throw new JsonSyntaxException(e);
      } catch (IllegalAccessException e) {
        throw new AssertionError(e);
      }
      in.endObject();
      //返回解析完成後的JavaBean物件
      return instance;
    }

實際上對於Type是集合類的時候,通過反射構建集合類物件在ConstructorConstructor類的newDefaultImplementationConstructor中做了處理,這裡就不多做說明了。
另外如果自定義的JavaBean中沒有預設構造器,那麼最終會呼叫ConstructorConstructor類中的newUnsafeAllocator方法來為你建立對應的JavaBean物件,如果此時建立失敗的話就會丟擲異常:

 throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
              + "Register an InstanceCreator with Gson for this type may fix this problem."), e);

Gson用反射解析json ,核心簡而言之就是通過反射建立JavaBean物件,迴圈遍歷該物件的Field,並通過Field的set(JavaBean,value)方法來對JavaBean的Field賦值。其實就是簡單的反射的應用,Gson只不過是做了簡單而有效的封裝處理來完成了這其中的操作。