輕觸開源(三)-Gson項目源碼解析_貳

轉載請註明出處:https://my.oschina.net/u/874727/blog/750473java

Q:1025250620數據結構

非墨上一篇文文章說到:Gson經過傳入的TypeToken類型,遍歷Gson變量中的factorys工廠,來生成一個TypeAdapter的轉換器。本章將細化這個生成過程。咱們依舊沿用上一次所定義的Json對象和Java數據模型,經過一個典型的例子來學習一下:在Gson項目中,是如何作到數據適配和轉換的。app

// code Java
public static class ClassRoom{
		public String roomName;
		public int number;
		public String toString() {
			return "["+roomName+":"+number+"]";
		}
	}
	
public static class User{
		public String name;
		public int age;
		private ClassRoom room;
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return name+"->"+age+":"+room;
		}
	}
...
//code main
String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);

對於上面的例子,在Gson中,Gson將會用專門的適配器RefrectiveTypeAdapter來轉換這種類型的數據。固然,這種適配器是頂層的適配器,對於裏面的name,age這些屬性,將會有不一樣的適配器來生成。整個適配器的解析結構相似於裝飾器。而用於構建RefrectiveTypeAdapter的工廠就是RefrectiveTypeAdapterFactory。Factory對TypeToken進行攔截,攔截的方式就是看是否能生成相應的Adapter對象。爲了說明這點,咱們回到Gson的getAdapter的方法:ide

//code Gson
 for (TypeAdapterFactory factory : factories) {
    	...
        TypeAdapter<T> candidate = factory.create(this, type);
        ...
}

能夠看出,Json是經過factory的create方法來判斷,工廠是否知足構建的條件。同時,咱們還能夠獲得另一個結論:若是一個Json串能夠被多個的factory.create方法攔截的話,那麼他們是有優先順序的。好比說對於String對象,它自己具備對象的屬性,所以它能夠被RefrectiveTypeAdapterFactory工廠所攔截。可是,因爲TypeAdapters.STRING_FACTORY的攔截器位於RefrectiveTypeAdapterFactory的前面,因此String對象並不會選擇RefrectiveTypeAdapterFactory,而選擇TypeAdapters中的STRING_FACTORY攔截器。咱們先來看一下RefrectiveTypeAdapterFactory的create方法是如何攔截的:函數

//code ReflectiveTypeAdapterFactory.java
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    Class<? super T> raw = type.getRawType();

    if (!Object.class.isAssignableFrom(raw)) {//code #1
      return null; // it's a primitive!
    }

    ObjectConstructor<T> constructor = constructorConstructor.get(type);
    //code #2
    return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
  }

在code#1中,Factory作了攔截判斷,而知足RefrectiveTypeAdapterFactory的構造條件是:工具

!Object.class.isAssignableFrom(raw)。對於isAssignableFrom方法的解釋,咱們看一下文檔:學習

/** object is either the same as, or is a superclass or
* superinterface of, the class or interface represented by the specified*/

根據文檔的解釋,咱們能夠知道上面的判斷語句是在判斷raw是不是直接或者間接繼承於Object或者自己就爲一個Object(接口類型也返回true)。這主要是爲了攔截一些基本類型,好比int和long。對於String對象的話也"能夠"被RefrectiveTypeAdapterFactory攔截。可是就像非墨說的,這種攔截是有優先級的,誰的攔截靠前,誰就優先被選擇。咱們來看下在Gson中所定義的factory攔截順序:ui

// code Gson.init()
{
...
factories.add(TypeAdapters.STRING_FACTORY);
...
factories.add(new ReflectiveTypeAdapterFactory(
        constructorConstructor, fieldNamingPolicy, excluder));
...
}

能夠看出,對於String類型來講,因爲TypeAdapters.STRING_FACOTRY的工廠攔截器更加靠前,所以對於特殊的基本類型String來講,仍是會選擇位於TypeAdapters中的基本類型工廠來處理。this

咱們繼續回到RefrectiveTypeAdapterFactory這個類的create代碼。在#code1 攔截完成以後,Factory構建了一個對象的構造器。上一篇(<輕觸開源(二)-Gson項目源碼解析_上>.net

https://my.oschina.net/u/874727/blog/749405)

咱們說到,Gson爲了方便操縱一些東西,而使用本身的數據結構定義類型中的一些定義。而這個ObjectConsturctor就是爲了方便對象構建而定義的構造器。

//code ObjectConstructor.java
 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);//#code1
    if (typeCreator != null) {//#code2
      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) {//#code2
      return new ObjectConstructor<T>() {
        public T construct() {
          return rawTypeCreator.createInstance(type);
        }
      };
    }

    ObjectConstructor<T> defaultConstructor = 
           newDefaultConstructor(rawType);//#code3
    if (defaultConstructor != null) {
      return defaultConstructor;
    }

    ObjectConstructor<T> defaultImplementation = 
        newDefaultImplementationConstructor(type, rawType);//#code4
    if (defaultImplementation != null) {
      return defaultImplementation;
    }

    // finally try unsafe
    return newUnsafeAllocator(type, rawType);//#code5
  }

在#code1 中,Gson默認也用了緩衝區instanceCreators攔截。可是因爲instanceCreators默認狀況下爲空集,因此咱們直接跳過。可是非墨但願你們記錄一下這個代碼段,緣由是咱們在經過GsonBuilder構造Gson對象的時候,還會提到它。#code3裏面,它會調用咱們默認聲明的構造器。而#code4裏會讓Gson幫你選定一些類型和構造器。爲何要讓Gson幫你選定類型和構造器呢?這是由於你不少狀況下你並不關心你的具體類型是什麼,而這種狀況經常用於傳入接口類型:

String strJsonArr = "[{name:'david',age:19,room:{roomName:'small',number:1}},"
				+ " {name:'xdf',age:18,room:{roomName:'big',number:2}}]";
		List<User> list = gson.fromJson(strJsonArr, new TypeToken<List<User>>(){}.getType());

這個場景中,咱們並無傳入具體類型,而是傳入一個集合類接口List<User>。咱們並不關心Gson返回的具體類型是什麼,只但願返回的是一個集合類。爲了解決這個問題,Gson會默認幫咱們選定一個具體類型。這就是#code4的具體功能。咱們回到剛纔#code3的代碼,Gson會優先調用類型的默認構造方法,而調用的方式是經過newDefaultConstructor方法。

private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
    try {
      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() {
          try {
            Object[] args = null;
            return (T) constructor.newInstance(args);
          } catch (e) {
               ...
          } 
        }
      };
    } catch (NoSuchMethodException e) {
      return null;
    }
  }

實際上,newDefaultConstructor方法就是獲取一下Type的無參數構造器。然而,若是咱們要用咱們本身的構造方法呢?還記得非墨上面讓各位看官記錄一下的instanceCreators麼?是的,咱們就須要用到它,可是這部分,非墨將在後面說GsonBuilder的時候來描述它。

代碼描述到這裏,咱們已經得到了Type的構造器,也就是ReflectiveTypeAdapterFactory的代碼咱們已經進行到了#code2的位置。

上面咱們將整個流程簡單梳理了一遍,可是問題並無徹底解決。咱們知道對於User數據結構的構成,實際上分紅好幾個部分:

爲了構建User對象你須要構建name屬性,age屬性和對象room屬性。羅馬並非一天建成的,對象也不是一次生成的。而這個構建的流程和屬性裏Adapter的選定,都在RefrectiveTypeAdapterFactory的create的最後一行代碼中。

//code ReflectiveTypeAdapterFactory.java 
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
    ...
    return new Adapter<T>(constructor, getBoundFields(gson, type, raw));//#code1
}
//---
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
    Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
    if (raw.isInterface()) {
      return result;
    }

    Type declaredType = type.getType();
    while (raw != Object.class) {
      Field[] fields = raw.getDeclaredFields();
      for (Field field : fields) {
        boolean serialize = excludeField(field, true);//#code1
        boolean deserialize = excludeField(field, false);//code2
        if (!serialize && !deserialize) {
          continue;
        }
        field.setAccessible(true);
        Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
        BoundField boundField = createBoundField(context, field, getFieldName(field),
            TypeToken.get(fieldType), serialize, deserialize);
        BoundField previous = result.put(boundField.name, boundField);
        if (previous != null) {
          throw new IllegalArgumentException(declaredType
              + " declares multiple JSON fields named " + previous.name);
        }
      }
      type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
      raw = type.getRawType();
    }
    return result;
  }

RefrectiveTypeAdapterFactory.Adapter的生成,會調用getBoundsField方法,這個方法的目的就是爲了分解對象中的屬性參數,並以屬性名和屬性結構映射的方式返回。在Gson中,它會獲取該對象的全部屬性,並記錄在本身的數據結構中。這樣的好處在於,你可使用繼承的方式來定義你本身的對象。Gson內部的Field數據結構是BoundField類。BoundField類是一個抽象類,分別有如下屬性:

this.name = name;//屬性名
this.serialized = serialized;//須要序列化
this.deserialized = deserialized;//可反序列化

而這些屬性均可以經過註解的方式往對象類中配置,應用於不一樣的應用場景。咱們看到上面的代碼#code1,Gson獲取傳入類的所有Field,而後經過excludeField方法來解析後兩個屬性。

public boolean excludeField(Field f, boolean serialize) {
    return excludeField(f, serialize, excluder);
  }

  static boolean excludeField(Field f, boolean serialize, Excluder excluder) {
    return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
  }

第二個參數serialize用於當前是序列化仍是反序列化,而後採用不一樣的程序邏輯。這部分代碼就跟它所定義的方法名字同樣,爲了排除掉一些沒必要要的屬性定義。對於那些不須要序列化和反序列化的屬性,Gson是不作的備案。

Type fieldType = $Gson$Types
    .resolve(type.getType(), raw, field.getGenericType());// #code1
BoundField boundField = createBoundField(context, field, getFieldName(field),
            TypeToken.get(fieldType), serialize, deserialize);// #code2
BoundField previous = result.put(boundField.name, boundField);

識別完該屬性是否須要序列化以後,對於要備案的屬性,就要生成對應的BoundField對象。Gson經過工具類$Gson$Types來解析子域的類型,記錄爲fieldType。看官是否會感受很奇怪,既然你已經獲得了field,直接獲取類型不就行了麼?爲何要畫蛇添足解析類型呢?回答這個問題咱們必需要回到<輕觸開源>系列的第一章:輕觸開源(一)-Java泛型Type類型的應用和實踐

https://my.oschina.net/u/874727/blog/747427

Field的類型若是是泛型的話,因爲它並不屬於聲明泛型的接口,所以,當Field的類型是一個泛型參數的時候,它的類型將依賴於外部類型。爲了說明這點,咱們把咱們以前的Java模型稍微轉換一下:

public static class ClassRoom{
		public String roomName;
		public long number;
		public String toString() {
			return "["+roomName+":"+number+"]";
		}
	}
	
	public static class User<T>{
		private T room;
		public String name;
		public int age;
		
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return name+"->"+age+":"+room;
		}
	}
     User<ClassRoom> usr = new User<ClassRoom>();

咱們能夠看到,對於新的對象模型,room的類型的傳入依賴於User類的泛型參數T。針對這個例子,咱們剛纔調用#code1中的參數分別對應的值就是:

Type fieldType = $Gson$Types
    .resolve(
type.getType(), // class Test2$User<Test2$ClassRoom>
raw, // Test2$User
field.getGenericType() //T
);// #code1

因爲子域field中的類型爲泛型參數T,所以它的具體類型依賴於類型自己的泛型參數。咱們根據這個例子來看下Gson是如何解析的。

public static Type resolve(
     Type context, //域的上下文,即直接包含在外側的類型
     Class<?> contextRawType, 
     Type toResolve//域定義的類型
) {
    // this implementation is made a little more complicated in an attempt to avoid object-creation
    while (true) {
      if (toResolve instanceof TypeVariable) {//case1
        TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
        toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
        if (toResolve == typeVariable) {
          return toResolve;
        }

      } else if(...){
        ...
      } else {
        return toResolve;
      }
    }
  }

根據咱們第一章對泛型的解釋,咱們能夠清楚的知道,對於T類型的變量,所傳入的Field的Type類型是TypeVariable類型。所以程序將經過case1條件語句,這就進入到resolveTypeVariable方法。

static Type resolveTypeVariable(
Type context, 
Class<?> contextRawType,
TypeVariable<?> unknown//#code1 
) {
    Class<?> declaredByRaw = declaringClassOf(unknown);//#code2
    /**//#code declaringClassOf
        private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
        GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
          return genericDeclaration instanceof Class
          ? (Class<?>) genericDeclaration
          : null;
        }
    */

    // we can't reduce this further
    if (declaredByRaw == null) {
      return unknown;
    }

    Type declaredBy = 
   getGenericSupertype(context, contextRawType, declaredByRaw);//#code3
    if (declaredBy instanceof ParameterizedType) {//#code4
      int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
      return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
    }

    return unknown;
  }

代碼的參數名再次幫咱們驗證了咱們的猜想,unknown正好說明這個函數就是在field類型未知的狀況下所進行的操做。#code2中函數declaringClassOf的代碼已經在註釋中展開。這個函數的目的是爲了返回一個GenericDeclaration的接口。而這個接口,相信各位看官並不陌生。若是須要回顧的話請查看個人第一篇文章:<輕觸開源(一)-Java泛型Type類型的應用和實踐>

https://my.oschina.net/u/874727/blog/747427

第一章咱們說到,GenericDeclaration是用於標識那些能夠用於泛型聲明的接口。所以咱們不難推斷出,對於上述咱們的例子中,變量room的T類型的聲明就是源自於User類。所以,declaringClassOf所返回的就是User的Type。接着#code3和#code4的代碼就很淺顯了。#code3用於得到User的全部泛型參數,而#code4找到這些泛型參數中所對應的T的實際類型。

好的,咱們大概對Gson解析子域類型有了基本的流程,咱們再次回到BoundField的構造代碼:

Type fieldType = $Gson$Types
    .resolve(type.getType(), raw, field.getGenericType());// #code1
BoundField boundField = createBoundField(context, field, getFieldName(field),
            TypeToken.get(fieldType), serialize, deserialize);// #code2
BoundField previous = result.put(boundField.name, boundField);

在#code2中,RefrectiveTypeAdapterFactory經過方法$Gson$Types
    .resolve獲得的Field的具體fieldType,來生成Gson本身的內部對象BoundField。這裏咱們能夠注意一下getFieldName方法,咱們知道Gson中的序列化名字是能夠配置的。而它的實現就隱藏在這個方法中。

static String getFieldName(FieldNamingStrategy fieldNamingPolicy, Field f) {
    SerializedName serializedName = f.getAnnotation(SerializedName.class);
    return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
  }

getFieldName方法中,咱們能夠看到,在Gson獲取Field的name的時候,會優先查看在你的Field上是否包含有@SerializedName註解,若是有的話將使用註解裏的Value值。若是沒有的話,將使用Gson本身的命名策略,裏面的策略有不少種,能夠在GsonBuilder中配置,本章就不展開講,有興趣的看官能夠本身研究。那麼咱們再回到上面的#code2中,接下去程序將經過createBoundField方法來構造一個BoundField:

return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
      final TypeAdapter<?> typeAdapter = 
      getFieldAdapter(context, field, fieldType);//#code1
      /**code getFieldAdapter()
      private TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
         JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);//#code1.1
         if (annotation != null) {
         TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation);
          if (adapter != null) return adapter;
         }
         return gson.getAdapter(fieldType);
      }
      */
      @Override void write(JsonWriter writer, Object value){
        TypeAdapter t =
          new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
        t.write(writer, fieldValue);
      }
    
      @Override void read(JsonReader reader, Object value)
          throws IOException, IllegalAccessException {
        Object fieldValue = typeAdapter.read(reader);
        if (fieldValue != null || !isPrimitive) {
          field.set(value, fieldValue);
        }
      }
 
      public boolean writeField(Object value) throws IOException, IllegalAccessException {
        if (!serialized) return false;
        Object fieldValue = field.get(value);
        return fieldValue != value; // avoid recursion for example for Throwable.cause
      }
    };
  }

createBoundField方法裏,會構造一個匿名的BoundField對象。其中,會經過調用(#code1)getFieldAdapter方法來生成BoundField的TypeAdapter。而getFieldAdapter的代碼,非墨已經直接粘到註釋中。在getFieldAdapter中,Gson會先去查看你的Field中是否標記有JsonAdapter的註解。這個註解的值必須是Class對象,而Class對象的限定條件必須是TypeAdapter類型或者是TypeAdapterFactory類型。若是Field中並無標記JsonAdapter註解,那麼Gson會調用Gson類中的getAdapter方法返回默認的Adapter。或許有些看官對這些概念看的雲裏霧裏。沒關係,非墨用一段代碼來帶各位實踐一下。咱們依舊對以前所定義的Java對象User進行改造,而且定義咱們本身的一個TypeAdapter:

public static class User<T>{
		@JsonAdapter(MyAdapter.class)
		private T room;
		public String name;
		public int age;
		
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return name+"->"+age+":"+room;
		}
}
//class MyAdapter
public class MyAdapter extends TypeAdapter<String> {

	@Override
	public void write(JsonWriter out, String value) throws IOException {}

	@Override
	public String read(JsonReader in) throws IOException {
		StringBuilder builder = new StringBuilder();
		in.beginObject();
		builder.append(in.nextName()).append("-");
		builder.append(in.nextString()).append("-");
		builder.append(in.nextName()).append("-");
		builder.append(in.nextString());
		in.endObject();
		return builder.toString();
	}

}

咱們經過上述的JsonAdapter註解來指定room類型所須要的適配器MyAdapter。這個適配器咱們返回一個String類型。也就是說,最後被注入到User的room變量中的類型是String類型。咱們打印下最後的結果獲得:

Gson gson = new Gson();
		String strJsonArr = "{name:'david',age:19,room:{roomName:'small',number:1}}";
		User<ClassRoom> usr = gson.fromJson(strJsonArr, new TypeToken<User<ClassRoom>>(){}.getType());
		System.out.println(usr);
//輸出"david->19:roomName-small-number-1"

各位看官是否還對上面的執行代碼感到疑惑?首先我經過泛型已經指定了room變量爲ClassRoom類型,可是你返回的是String類型,爲何可以注入的你的對象中,而且你的程序並無出錯?

這個問題咱們又得回到本系列的第一章,非墨一直在強調,對於泛型的處理,java虛擬機只是將它控制在編譯期。在執行期的時候,無非就是在特定的狀況下加入了一下classcast的指令。若是咱們將usr.room的class打印一下,這個時候Java編譯器會在此次調用以後進行一個classCast的操做,這時候,程序纔會中斷拋出異常。

System.out.println("user = "+usr.room.getClass());
//輸出 Exception in thread "main" java.lang.ClassCastException

(待續)

相關文章
相關標籤/搜索