轻触开源(二)-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就是经过判断传入的对象类型是不是集合类型来进行选择。

(待续)

相关文章
相关标签/搜索