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只不過是做了簡單而有效的封裝處理來完成了這其中的操作。