轉載請註明出處: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
(待續)