輕觸開源(二)-Gson項目源碼解析_壹

上篇文章<輕觸開源-Java泛型Type類型的應用和實踐(一)>java

http://www.javashuo.com/article/p-pzkygieh-dn.htmljson

非墨寫到JAVA的泛型機制,被用到不少的開源項目。在衆多的開源項目中,Gson是很具備表明性的一個。Gson是Google公司編寫的一套用於Json數據轉化爲Java對象的一套通用工具庫。之因此說它是通用工具庫,是由於它的實現代碼所有基於最基礎的Java運行時環境,而不依賴於任何系統平臺,也就是說你不只能夠在J2EE項目中應用它,你同樣能夠很容易的在Android,J2ME等等平臺中直接應用它。緩存

Gson跟不少的開源操縱了Java內部數據類型的項目相同,爲了方便記錄類型數據,Gson會將Java原有的一套數據類型,轉化爲本身的內部數據類型。好比,在上一章咱們提到的在Java泛型中記錄類型的Class和Type類型,就被Gson轉化爲TypeToken。WildcardType轉化爲Gson本身的WildcardTypeImpl,GenericArrayType轉爲了Gson的內部類型GenericArrayTypeImpl。而這些類型的定義都被記錄在com.google.gson.internal包中。咱們從這個包名也看的很明白,就是Gson系統將一些轉換的細節屏蔽到Gson項目的內部,而只暴露給用戶一些簡單的接口。安全

但不論Gson如何轉變既定的Java類型,實際上都只是在Java的既定類型外加一層殼,能夠說是一個類適配器,好比咱們來看一下WildcardTypeImpl的代碼:多線程

private static final class WildcardTypeImpl implements WildcardType, Serializable {
    private final Type upperBound;
    private final Type lowerBound;

    public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
      checkArgument(lowerBounds.length <= 1);
      checkArgument(upperBounds.length == 1);

      if (lowerBounds.length == 1) {
        checkNotNull(lowerBounds[0]);
        checkNotPrimitive(lowerBounds[0]);
        checkArgument(upperBounds[0] == Object.class);
        this.lowerBound = canonicalize(lowerBounds[0]);
        this.upperBound = Object.class;

      } else {
        checkNotNull(upperBounds[0]);
        checkNotPrimitive(upperBounds[0]);
        this.lowerBound = null;
        this.upperBound = canonicalize(upperBounds[0]);
      }
    }

    public Type[] getUpperBounds() {
      return new Type[] { upperBound };
    }

    public Type[] getLowerBounds() {
      return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
    }

    @Override public boolean equals(Object other) {
      return other instanceof WildcardType
          && $Gson$Types.equals(this, (WildcardType) other);
    }

    @Override public int hashCode() {
      // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
      return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
          ^ (31 + upperBound.hashCode());
    }

    @Override public String toString() {
      if (lowerBound != null) {
        return "? super " + typeToString(lowerBound);
      } else if (upperBound == Object.class) {
        return "?";
      } else {
        return "? extends " + typeToString(upperBound);
      }
    }

    private static final long serialVersionUID = 0;
  }

WildcardType對象中最重要的upper和lower參數,實際上都是由外部對象傳入,在Gson的WildcardTypeImpl內部,不過是作了一層適配器。好的,咱們先預熱到這裏,咱們進入咱們今天的主題Gson的源碼。ide

在咱們深刻講Gson源碼以前,咱們先用一下Gson這個庫,領略一下它的魅力。所以咱們先構建兩個基礎的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;
		}
	}

模型是用於記錄用戶信息以及班級信息。爲了映射這個對象數據,咱們編寫一個簡單Json字符:工具

String strJson = "{name:'david',age:19,room:{roomName:'small',number:1}}";
User u = gson.fromJson(strJson, User.class);

最後咱們能夠將生成的u對象打印一下獲得:測試

david->19:[small:1]

各位看官是否被驚豔到?是的,使用Gson就是能夠這麼容易的轉換Json對象。雖然咱們還沒開始閱讀Gson的源代碼,可是咱們能夠從傳入的參數簡單看出,在Gson的實現中,必定是大量用到了Java的反射注入技術。咱們看下Gson的分包:ui

gson的分包很簡單,從名字上看,每一個包分類的目的也都很明確。在Gson中,從普通的Json對象到Gson對象的轉換,是經過internal包及其子包bind中的適配器TypeAdapter完成的。而這種完成的類型數據,是依賴於reflect中記錄的Type信息來完成的。適配器所須要的輸入源或者輸出源,是來自於stream包的數據流。當你的對象模型有一些特殊的輸出需求或者輸入需求,能夠經過annotation包中的註解來操縱你的元數據。爲了說明這一切,咱們回頭看一下咱們的測試代碼,在代碼中,咱們是直接調用了Gson.fromJson方法。當咱們跟蹤fromJson這個方法到最後,咱們會發現Gson.fromJson方法最終會調用到方法塊:

//file:"com/google/gson/Gson.java"
 @SuppressWarnings("unchecked")
  public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    JsonReader jsonReader = new JsonReader(json);// step1
    T object = (T) fromJson(jsonReader, typeOfT);
    assertFullConsumption(object, jsonReader);
    return object;
  }


@SuppressWarnings("unchecked")
  public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
    boolean oldLenient = reader.isLenient();
    reader.setLenient(true);
    try {
      reader.peek();//step2
      isEmpty = false;
      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);//step3
      TypeAdapter<T> typeAdapter = getAdapter(typeToken);//step4
      T object = typeAdapter.read(reader);
      return object;
    } catch (EOFException e) {
      /*
       * For compatibility with JSON 1.5 and earlier, we return null for empty
       * documents instead of throwing.
       */
      if (isEmpty) {
        return null;
      }
      throw new JsonSyntaxException(e);
    } catch (IllegalStateException e) {
      throw new JsonSyntaxException(e);
    } catch (IOException e) {
      // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
      throw new JsonSyntaxException(e);
    } finally {
      reader.setLenient(oldLenient);
    }
  }

就像咱們上面說的同樣,在代碼#step1 中,Gson會將真實的字符IO流Reader裝飾成爲在stream包下的JsonReader流。在代碼#step3位置,Gson會經過Java既定的類型找到Gson所轉換的type類型(reflect包下的TypeToken對象)。而後經過這個類型調用代碼#step4的語句,獲取一個類型轉換的適配器TypeAdapter。適配器獲取到Reader輸入源以後,就能夠將Json數據轉化成爲對應的對象。

TypeToken採用一種懶加載的機制來生成TypeToken。這種機制在程序代碼中很是常見。

/**
   * Gets type literal for the given {@code Type} instance.
   */
  public static TypeToken<?> get(Type type) {
    return new TypeToken<Object>(type);
  }

 @SuppressWarnings("unchecked")
  protected TypeToken() {
    this.type = getSuperclassTypeParameter(getClass());
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
    this.hashCode = type.hashCode();
  }

  /**
   * Unsafe. Constructs a type literal manually.
   */
  @SuppressWarnings("unchecked")
  TypeToken(Type type) {
    this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
    this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
    this.hashCode = this.type.hashCode();
  }

咱們能夠看到,TypeToken.get方法,其實是生成了一個TypeToken對象。而對於TypeToken對象的生成,在TypeToken類中有兩種構造方式。第一種無參數的構造方式的做用域設置爲protected,意味着你必需要經過繼承的方式才能使用它,而且所須要轉化的類型須要經過繼承裏的泛型參數指定。而第二種構造方法須要傳入一個Type對象。而這個Type對象就是咱們上一篇文章中的Type對象(四種直接子接口和一個實現類)。而Java中的Type類型到Gson中的對象映射,就由$Gson$Type的canonicalize方法完成:

public static Type canonicalize(Type type) {
    if (type instanceof Class) {
      Class<?> c = (Class<?>) type;
      return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;

    } else if (type instanceof ParameterizedType) {
      ParameterizedType p = (ParameterizedType) type;
      return new ParameterizedTypeImpl(p.getOwnerType(),
          p.getRawType(), p.getActualTypeArguments());

    } else if (type instanceof GenericArrayType) {
      GenericArrayType g = (GenericArrayType) type;
      return new GenericArrayTypeImpl(g.getGenericComponentType());

    } else if (type instanceof WildcardType) {
      WildcardType w = (WildcardType) type;
      return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());

    } else {
      // type is either serializable as-is or unsupported
      return type;
    }
  }

Gson在將數據源傳入給適配器Adapter作轉換操做的以前,會有一個peek操做。peek跟咱們日常用到的流的peek沒有什麼差別,都是不彈出流數據的狀況下查看緩衝區數據。這個peek操做的目的是爲了肯定以何種輸入源類型來處理Json對象。好比,當你輸入的JSON串爲:

{name:david}

的時候,因爲輸入員peek出來的是一個"{"字符。在JsonReader對象中,會以"PEEKED_BEGIN_OBJECT" 變量標記peek狀態量。而若是你的Json數據是:

[{name:david},{name:Lily}]

的話,因爲JsonReader.peek出來的數據是"["字符,所以peek狀態量會標記爲"PEEKED_BEGIN_ARRAY"。

public JsonToken peek() throws IOException {
    int p = peeked;
    if (p == PEEKED_NONE) {
      p = doPeek();
      
    }
    ....
    default:
      throw new AssertionError();
    }
  }

因爲JsonReader咱們是剛剛生成,所以peeked狀態量的默認值,也就是PEEKED_NONE.這樣,程序就跳轉到函數doPeek()中。

private int doPeek() throws IOException {
    int peekStack = stack[stackSize - 1];//狀態棧
    ...
}

JsonReader使用棧式的解析,stack存放JsonScope常量所枚舉的對象。這個棧中主要存放解析過程當中所操縱的對象類型。因爲目前Json解析還沒有開始,目前棧中存放的是默認值"EMPTY_DOCUMENT"

final class JsonScope {

    /**
     * An array with no elements requires no separators or newlines before
     * it is closed.
     */
    static final int EMPTY_ARRAY = 1;

    /**
     * A array with at least one value requires a comma and newline before
     * the next element.
     */
    static final int NONEMPTY_ARRAY = 2;

    /**
     * An object with no name/value pairs requires no separators or newlines
     * before it is closed.
     */
    static final int EMPTY_OBJECT = 3;

    /**
     * An object whose most recent element is a key. The next element must
     * be a value.
     */
    static final int DANGLING_NAME = 4;

    /**
     * An object with at least one name/value pair requires a comma and
     * newline before the next element.
     */
    static final int NONEMPTY_OBJECT = 5;

    /**
     * No object or array has been started.
     */
    static final int EMPTY_DOCUMENT = 6;

    /**
     * A document with at an array or object.
     */
    static final int NONEMPTY_DOCUMENT = 7;

    /**
     * A document that's been closed and cannot be accessed.
     */
    static final int CLOSED = 8;
}

以後根據讀入的第一個字符"{"或者"["返回具體的類型,對於"{"字符,將返回一個"PEEKED_BEGIN_OBJECT"類型。

private void doPeek() {
...
int c = nextNonWhitespace(true);//取得第一個非空白字符
    switch (c) {
    ...
    
    case '[':
      return peeked = PEEKED_BEGIN_ARRAY;
    case '{':
      return peeked = PEEKED_BEGIN_OBJECT;
    }
...

}

但從代碼功能上來看,實際上講Reader.peek代碼放在Adapter.read代碼前的任何位置都不影響邏輯。咱們再繼續以前的代碼段:

//com.google.gson.Gson fromJson()
 1.reader.peek();
 2.isEmpty = false;
 3.TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
 4.TypeAdapter<T> typeAdapter = getAdapter(typeToken);
 5.T object = typeAdapter.read(reader);
 6.return object;

第1行代碼,咱們經過peek來記錄一下Json的最外層對象類型

第3行代碼,咱們用過傳入的類型來生成了一個Gson的類型對象TypeToken

第4行代碼,咱們經過第三行代碼生成的TypeToken對象生成一個數據轉換的適配器

第5行代碼,咱們經過適配器,將輸入源中的Json數據轉換爲Java中的數據對象

上面咱們已經說到了第三行代碼,TypeToken.get方法調用後,new了一個TypeToken對象。

// code1
@SuppressWarnings("unchecked")
  public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    TypeAdapter<?> cached = typeTokenCache.get(type);//#1 緩存TypeAdapter
    if (cached != null) {
      return (TypeAdapter<T>) cached;
    }

    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();//#2 線程安全
    boolean requiresThreadLocalCleanup = false;
    if (threadCalls == null) {
      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
      calls.set(threadCalls);
      requiresThreadLocalCleanup = true;
    }

    // the key and value type parameters always agree
    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
    if (ongoingCall != null) {
      return ongoingCall;
    }

    try {
      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();//#3無用的類裝飾
      threadCalls.put(type, call);
      for (TypeAdapterFactory factory : factories) {
        TypeAdapter<T> candidate = factory.create(this, type);//#4查找對應的工廠
        if (candidate != null) {
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }
      throw new IllegalArgumentException("GSON cannot handle " + type);
    } finally {
      threadCalls.remove(type);

      if (requiresThreadLocalCleanup) {
        calls.remove();
      }
    }
  }

Gson在獲取TypeAdapter的時候,會先從線程的Cache中去取,代碼#1很好的詮釋了這一點。而爲了保證在多線程狀態下的狀態穩定性,Gson給每一個線程都設定了一個Map類型的Cache。#2以後的代碼就是在完成這麼一項工做。在#3代碼裏Gson引入了一個新的類FutureTypeAdapter。這個類實際上沒有什麼實際上的意義,因此能夠忽略它。在代碼#4的時候,Gson經過遍歷本身的factories列表來生成一個TypeAdapter對象。實際上在這一步,Gson在作一個Factory選擇,咱們來看一個Factory的例子:

// code CollectionTypeAdapterFactory.java
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
    Type type = typeToken.getType();

    Class<? super T> rawType = typeToken.getRawType();
    if (!Collection.class.isAssignableFrom(rawType)) { #1 集合類判斷
      return null;
    }
    ...
    TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
    return result;
  }

這是一個集合類TypeAdapter生成的例子。代碼#1就是經過判斷傳入的對象類型是不是集合類型來進行選擇。

(待續)

相關文章
相關標籤/搜索