转载请注明出处:https://my.oschina.net/u/874727/blog/750473java
Q:1025250620数据结构
非墨上一篇文文章说到:Gson经过传入的TypeToken类型,遍历Gson变量中的factorys工厂,来生成一个TypeAdapter的转换器。本章将细化这个生成过程。咱们依旧沿用上一次所定义的Json对象和Java数据模型,经过一个典型的例子来学习一下:在Gson项目中,是如何作到数据适配和转换的。app
// 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的方法:ide
//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拦截顺序:ui
// code Gson.init() { ... factories.add(TypeAdapters.STRING_FACTORY); ... factories.add(new ReflectiveTypeAdapterFactory( constructorConstructor, fieldNamingPolicy, excluder)); ... }
能够看出,对于String类型来讲,因为TypeAdapters.STRING_FACOTRY的工厂拦截器更加靠前,所以对于特殊的基本类型String来讲,仍是会选择位于TypeAdapters中的基本类型工厂来处理。this
咱们继续回到RefrectiveTypeAdapterFactory这个类的create代码。在#code1 拦截完成以后,Factory构建了一个对象的构造器。上一篇(<轻触开源(二)-Gson项目源码解析_上>.net
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
(待续)