轻触开源(三)-Gson项目源码解析_贰

久未见 提交于 2019-12-06 14:01:20

转载请注明出处:https://my.oschina.net/u/874727/blog/750473

Q:1025250620

非墨上一篇文文章说到:Gson通过传入的TypeToken类型,遍历Gson变量中的factorys工厂,来生成一个TypeAdapter的转换器。本章将细化这个生成过程。我们依旧沿用上一次所定义的Json对象和Java数据模型,通过一个典型的例子来学习一下:在Gson项目中,是如何做到数据适配和转换的。

// 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的方法:

//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拦截顺序:

// code Gson.init()
{
...
factories.add(TypeAdapters.STRING_FACTORY);
...
factories.add(new ReflectiveTypeAdapterFactory(
        constructorConstructor, fieldNamingPolicy, excluder));
...
}

可以看出,对于String类型来说,由于TypeAdapters.STRING_FACOTRY的工厂拦截器更加靠前,因此对于特殊的基本类型String来说,还是会选择位于TypeAdapters中的基本类型工厂来处理。

我们继续回到RefrectiveTypeAdapterFactory这个类的create代码。在#code1 拦截完成之后,Factory构建了一个对象的构造器。上一篇(<轻触开源(二)-Gson项目源码解析_上>

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

(待续)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!