Gson 源码分析

Android · timqi · 于 发布 · 502 次阅读
96

原文链接:http://www.timqi.com/2017/06/14/gson-source-analysis/

如今使用 Json 数据格式通信几乎是再平常不过的事情,以至于都忘了去研究 Json 字符串与 Object 之间到底是怎样转换的,只记得简单的调用几个简单的 API。Gson 是 Java 语言的 Json 库,通常也是 Android 平台的首选方案,org.json(Android 内建 json 库) 相比更加底层,适合编写 toJson() 类函数或者对 Java 反射有限制的平台。深入 Gson 代码发现简单的 Json 解析也有如此多的可圈可点之处,Gson 的架构方案非常优美,结构清晰扩展性强,本文对此展开分析。

本文分析代码基于版本 3090a7ed2a。文章篇幅有限希望读者下载相应代码以观全貌而不是仅仅限于本文列出的代码。

首先 Gson 对象提供了两种构建方法,一种是默认参数的 Constructor,new Gson(),我们可以看到有数不清的配置参数,通过 new Gson() 我们可以快速得到一个开箱即用的默认 Gson 对象;同时为了满足自定义策略 Gson 使用 Builder 模式提供了另一种构建方法, 通过 GsonBuilder 可以调节 Gson 对象的细节参数,具体请阅读 GsonBuilder,最终在 GsonBuilder#create 方法中通过包内权限的 Gson 默认构建函数得到 Gson 对象。

Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy,
    final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
    boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
    boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
    LongSerializationPolicy longSerializationPolicy,
    List<TypeAdapterFactory> typeAdapterFactories) {

  ...

  factories.add(new ReflectiveTypeAdapterFactory(
      constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
}

对于 LongSerializationPolicy,FieldNamingStrategy,ExclusionStrategies,SerializationExclusionStrategy, DeserializationExclusionStrategy 等不同方案使用了枚举,策略模式进行封装,如在系列化 Long 类型值时是用 long 还是 字符型策略:

public enum LongSerializationPolicy {
  DEFAULT() {
    @Override public JsonElement serialize(Long value) {
      return new JsonPrimitive(value);
    }
  },

  STRING() {
    @Override public JsonElement serialize(Long value) {
      return new JsonPrimitive(String.valueOf(value));
    }
  };

  public abstract JsonElement serialize(Long value);
}

Gson 对象中内建了解析不同 Java 类型的 TypeAdpater 分别处理不同类型的 Json 结构。然后 在 fromJson, toJson 方法中调用 Adapter 的 read, write 方法递归的解析出不通层次 json 值。同时允许用户在 GsonBuilder 中注册自定义的 Adpater 完成高级操作,这是 Gson 高扩展性的原因。

Gson 中重载了多种参数的 toJson,fromJson 函数,但最终落地到这两个函数上,然后将数据的系列化与反序列化逻辑移交到 Adapter 的 write 与 read 函数中。

public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
  ...
  try {
    reader.peek();
    isEmpty = false;
    TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
    TypeAdapter<T> typeAdapter = getAdapter(typeToken);
    T object = typeAdapter.read(reader);
    return object;
  } catch (Exception e) {
    ...
  }
} 

public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
  TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
  ...
  try {
    ((TypeAdapter<Object>) adapter).write(writer, src);
  } catch (Exception e) {
    ...
  }
}

其中通过 getAdpater 函数获取到对应类型的 Adapter 对象,getAdapter 中做了很多优化。首先 typeTokenCache 是一个线程安全的 Map 对象,key 为对象的类型,value 为对应类型的 Adpater,如果在 typeTokenCache 中取到了对应的 Adapter 则返回不进行后续逻辑。如果 typeTokenCache 中没有对应 Adpater,同时考虑到线程安全,Gson 采用了 ThreadLocal 的方案解,从 calls 中取出当前线程的 Map 对象 threadCalls,如果 threadCalls 中能获取到相应类型的 Adpater 则返回,否则将当前线程的特定 Type 的 Adapter 设置为 FutureTypeAdapter,
接下来遍历所有已经注册的 AdapterFactory 的对象 factories,如果构造出对应类型的 Adpater 则把 FutureTypeAdapter 代理到相应的 Adapter 上并缓存近 typeTokenCache 提高下次访问性能。

public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
  TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
  if (cached != null) {
    return (TypeAdapter<T>) cached;
  }

  Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
  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>();
    threadCalls.put(type, call);

    for (TypeAdapterFactory factory : factories) {
      TypeAdapter<T> candidate = factory.create(this, type);
      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();
    }
  }
}

通过 GsonBuilder 注册的 TypeAdater 在 GsonBuilder,最终在 GsonBuilder#create 函数中做为 factories 参数传入了默认 Gson 构造函数中,而注册的这些 factories 会比内建的几个 Adapter 更早加入 factories 中,结合上面遍历 factories 的代码我们可以看到越早加入则先返回,也就是说在 GsonBuilder 中注册的相同类型 TypeAdatper 可以覆盖掉内建的 Adapter 从而实现自定义。

至此我们看到 Gson 框架实际上把对象的序列化与反序列工作分发到不同类型的 TypeAdapter 中,具体的工作由 Adapter 的 write 与 read 函数完成。Gson 本身提供了强大的流操作工具与丰富的 Adpater 类型以至于即使不自定义也有良好的开箱即用体验。深入 Adapter 相关代码我们发现在 factories 中除了注册了一堆 Primitive 类型的 Adapter 外,最后还有一个 ReflectiveTypeAdapterFactory,这个 ReflectiveTypeAdapterFactory 通过反射将 Java 对象属性与 Json 字符紧密连接。

首先在 ReflectiveTypeAdapterFactory#create 方法中可以看到,ReflectiveTypeAdapterFactory 适用于除原始类型外所有的 Java 对象类型。当然如果在 GsonBuilder 中注册了特定类型除外。

@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
  Class<? super T> raw = type.getRawType();

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

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

...

Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
  this.constructor = constructor;
  this.boundFields = boundFields;
}

在 ReflectiveTypeAdapterFactory 的构建函数中获取到了对象的构建函数句柄有能力直接构建新对象,并且 boundFields 存储了这个对象的所有属性。

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);
      boolean deserialize = excludeField(field, false);
      if (!serialize && !deserialize) {
        continue;
      }
      field.setAccessible(true);
      Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
      List<String> fieldNames = getFieldNames(field);
      BoundField previous = null;
      for (int i = 0, size = fieldNames.size(); i < size; ++i) {
        String name = fieldNames.get(i);
        if (i != 0) serialize = false; // only serialize the default name
        BoundField boundField = createBoundField(context, field, name,
            TypeToken.get(fieldType), serialize, deserialize);
        BoundField replaced = result.put(name, boundField);
        if (previous == null) previous = replaced;
      }
      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;
}

getBoundFields 方法中获取到了一个类型的 BoundFields 并传给 ReflectiveTypeAdapter。我们可以在 getBoundFields 中处理了这个属性是否参与序列化,它的名字句柄信息并通过 createBoundField 方法结合在 Java 源码中的自定义注释等情况最终得到描述每个属性的 BoundField。createBoundField,BoundField 的结构如下:

private ReflectiveTypeAdapterFactory.BoundField createBoundField(
    final Gson context, final Field field, final String name,
    final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
  final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
  // special casing primitives here saves ~5% on Android...
  JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
  TypeAdapter<?> mapped = null;
  if (annotation != null) {
    mapped = jsonAdapterFactory.getTypeAdapter(
        constructorConstructor, context, fieldType, annotation);
  }
  final boolean jsonAdapterPresent = mapped != null;
  if (mapped == null) mapped = context.getAdapter(fieldType);

  final TypeAdapter<?> typeAdapter = mapped;
  return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
    @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
    @Override void write(JsonWriter writer, Object value)
        throws IOException, IllegalAccessException {
      Object fieldValue = field.get(value);
      TypeAdapter t = jsonAdapterPresent ? typeAdapter
          : new TypeAdapterRuntimeTypeWrapper(context, 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);
      }
    }
    @Override 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
    }
  };
}

static abstract class BoundField {
  final String name;
  final boolean serialized;
  final boolean deserialized;

  protected BoundField(String name, boolean serialized, boolean deserialized) {
    this.name = name;
    this.serialized = serialized;
    this.deserialized = deserialized;
  }
  abstract boolean writeField(Object value) throws IOException, IllegalAccessException;
  abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
  abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
}

BuildField 类通过递归的获取属性的 Adpater 并调用 read,write 方法描述了原来对象每个属性的反序列化与序列化怎么进行。至此我们不难猜出最后一步便是根据 BuildField 完成 ReflectiveTypeAdapter 的 read,write 方法便大功告成,ReflectiveTypeAdapter 的 read,write 方法如下:

@Override public T read(JsonReader in) throws IOException {
  if (in.peek() == JsonToken.NULL) {
    in.nextNull();
    return null;
  }

  T instance = constructor.construct();

  try {
    in.beginObject();
    while (in.hasNext()) {
      String name = in.nextName();
      BoundField field = boundFields.get(name);
      if (field == null || !field.deserialized) {
        in.skipValue();
      } else {
        field.read(in, instance);
      }
    }
  } catch (IllegalStateException e) {
    throw new JsonSyntaxException(e);
  } catch (IllegalAccessException e) {
    throw new AssertionError(e);
  }
  in.endObject();
  return instance;
}

@Override public void write(JsonWriter out, T value) throws IOException {
  if (value == null) {
    out.nullValue();
    return;
  }

  out.beginObject();
  try {
    for (BoundField boundField : boundFields.values()) {
      if (boundField.writeField(value)) {
        out.name(boundField.name);
        boundField.write(out, value);
      }
    }
  } catch (IllegalAccessException e) {
    throw new AssertionError(e);
  }
  out.endObject();
}

ReflectiveTypeAdapter 中对反序列化的处理遵循了以 Filed 属性为准的原则,如果在 Json 字符串中有值则赋予相应属性,如果没有则置空,由于预先遍历出了所有属性域所以解析过程复杂度为 O(N)。

由于 Adpater 递归的整体框架使得 Gson 具有很高的定制化能力,比如我们在 GsonBuilder 中注册 String.class 对应的 Adpater,而 GosnBuilder 中的 factories 比内建 Adapter 靠前实际上覆盖的内建的 String Adapter 的处理方案,因此我们对 Json 中的空字符串 "", null 值等做统一化处理简化后期数据使用避免潜在问题。

本文重点研究 Gson 源码的构建原理,如果要了解 Gson 库的一些高级用法请参考官方文档:https://github.com/google/gson/blob/3090a7ed2af4d6d2e6bd3647d12839bb4ad404e8/UserGuide.md,当然在了解原理的基础上看用法自然融会贯通。

原文链接:http://www.timqi.com/2017/06/14/gson-source-analysis/

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册