Mybatis 源码学习-反射工具(TypeParameterResolver)
Posted 凉茶方便面
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 源码学习-反射工具(TypeParameterResolver)相关的知识,希望对你有一定的参考价值。
历史文章 :
Mybatis 源码学习(2)-反射工具(Reflector)
TypeParameterResolver 是用来解析类中字段、方法参数、方法返回值的工具类,它通过自身的静态方法,利用反射处理对应的类型,并转化为实际的 Type 类型。而 Type 类型是 java 程序中所有数据类型的基类,它包含原始类型,参数化类型,类型变量,数组类型和通配符泛型几种类型。
Type
Type 是所有类型的父接口,它的继承关系如下:
Class:原始类型,Java 中的每个类都表现为一个 Class 对象,在程序中通过,类型.class、对象.getClass()或者 Class.forName(“类名”)创建 Class 对象。
ParameterizedType:参数化类型,如 List, Map<Integer, String>这种带有泛型的类型。
ParameterizedType 有三个常用方法:
-Type getRawType()
:返回该类型的原始类型,如 List -> List;
-Type[] getActualTypeArguments()
:返回泛型参数的实际类型,如List -> String;
-Type getOwnerType()
:返回类型的所属类型,如 Map.Entry<K, V> -> Map<K, V>;List -> null(List 不是内部类)。
TypeVariable:类型变量,它可以反应 JVM 编译泛型前的信息,在编译时需要转换为具体类型才能正常使用,如 List -> T 为类型变量。
TypeVariable 有三个常用方法:
-Type[] getBounds()
:获取类型变量继承关系的上边界,顶层为 Object,如 class Test
-> P 的上边界 Person;
-D getGenericDeclaration()
:获取类型变量的原始类型,如:class Test -> Test;
-String getName()
:获取类型变量的原始名称,如 class Test -> T。
GenericArrayType :数组类型,且元素类型为 ParameterizedType 和 TypeVariable 。如 List<String>[]
或 T[]
,它的 Type getGenericComponentType 方法返回数组的组成元素,如 List,T。
WildcardType:通配符泛型,如 List<? extends A> -> ? extends A。
WildcardType 有两个常用方法:
-Type[] getUpperBounds()
:返回泛型变量的上界;
-Type[] getLowerBounds()
:返回泛型变量的下界。
TypeParameterResolver
TypeParameterResolver 有非常多的 resolve 方法,但是主要就三个对外的方法:resolveFieldType(解析字段类型)、resolveParamTypes(解析参数类型)、resolveReturnType(解析返回值类型),它们均调用 resolveType 完成类型解析。
TypeParameterResolver.resolveFieldType 被用来解析类内部定义的字段的类型信息(同理还有resolveParamTypes 用于解析方法的参数、resolveReturnType 用于解析方法的返回值):
public static Type resolveFieldType(Field field, Type srcType)
// 获取字段的声明类型
Type fieldType = field.getGenericType();
// 获取字段定义所在的类的 Class 对象
Class<?> declaringClass = field.getDeclaringClass();
// 分析类型
return resolveType(fieldType, srcType, declaringClass);
resolveType 方法具有三个参数:字段、方法返回或方法参数的类型、字段定义的所在类。
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass)
if (type instanceof TypeVariable)
// 解析 TypeVariable 类型
return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
else if (type instanceof ParameterizedType)
// 解析 ParameterizedType 类型
return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
else if (type instanceof GenericArrayType)
// 解析 GenericArrayType 类型
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
else
return type; // Class 类型
// 字段、方法返回或方法参数的类型不可能是 WildcardType 类型,但是其泛型参数可能是 WildcardType 类型
resolveParameterizedType 的方法继续处理泛型参数上的类型信息(存在递归的情况):
// parameterizedType 是原始泛型参数的类型;
// srcType 是原始类型,是由外部传入的参数,一般是指类型所在的 class;
// declaringClass 是类型真实所在的类,如存在继承关系时,子类的方法可能仅在父类中定义
private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass)
// 返回参数类型数据的原始类型 Class 对象,如 List< T> -> List.class
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
// 获取实际的类型变量,如 T
Type[] typeArgs = parameterizedType.getActualTypeArguments();
// 保存结果
Type[] args = new Type[typeArgs.length];
// 解析参数类型(如,T,但是可能不止一个参数类型,如 Map<N, M>,会有 N,M 两个)
for (int i = 0; i < typeArgs.length; i++)
// 解析类型变量(如,List<T> -> T)
if (typeArgs[i] instanceof TypeVariable)
args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
else if (typeArgs[i] instanceof ParameterizedType)
// 解析嵌套 ParameterizedType(如,ClassA<ClassB<T>> -> ClassB<T>)
args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
else if (typeArgs[i] instanceof WildcardType)
// 解析嵌套 WildcardType
args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
else
args[i] = typeArgs[i];
// 解析结果封装成 TypeParameterResolver 中对 ParameterizedType 的实现类
return new ParameterizedTypeImpl(rawType, null, args);
resolveTypeVar 方法负责解析 TypeVariable ,它是类型变量上的参数,如类中存在字段 List -> T。
// typeVar 待解析的数据,即解析出来的参数的类型信息(如 Filed 上的泛型参数等)
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass)
Type result = null;
Class<?> clazz = null;
// 如果是类型所在的类(无嵌套的情况)
if (srcType instanceof Class)
clazz = (Class<?>) srcType;
else if (srcType instanceof ParameterizedType)
// 如果待解析类型仍然是泛型对象,则继续向下层继续解析
ParameterizedType parameterizedType = (ParameterizedType) srcType;
clazz = (Class<?>) parameterizedType.getRawType();
else
throw new IllegalArgumentException(…);
// 所在类和声明的类是同一个,即不是继承父类字段的情况
if (clazz == declaringClass)
Type[] bounds = typeVar.getBounds(); //获取上界
if(bounds.length > 0)
return bounds[0]; // 仅返回最近的上界
return Object.class;
// 检查直接父类
Type superclass = clazz.getGenericSuperclass();
// 这里存在递归向父类型进行检查
// 按照 index 查询对应的泛型参数的实际类型
// 其实就是字段上可能会有泛型参数(在父类中定义),泛型参数会由父类提供,子类会填补,
// 这里在做这个填补后的字段类型的解析。
// 如 ObjectA extends InterfaceA<String>,而 InterfaceA<T> 存在字段 List<T>(ObjectA 未覆盖),
// 那么解析 List<T> 中 T 的实际类型时,List<T> 的泛型参数 T 应该被解析为 String。
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
if (result != null)
return result;
// 检查直接父接口
Type[] superInterfaces = clazz.getGenericInterfaces();
for (Type superInterface : superInterfaces)
// 可能存在递归调用,解析情况同父类型
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
if (result != null)
return result;
return Object.class;
scanSuperTypes 如上所述,负责解析父类型、父接口中被子类覆盖的某些泛型参数的实际类型。
// typeVar、srcType、declaringClass 的定义还前面的一致
// clazz,待解析的类
// superclass,待解析的父类(父接口)
private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass)
Type result = null;
// 父类依然是泛型参数的类
if (superclass instanceof ParameterizedType)
ParameterizedType parentAsType = (ParameterizedType) superclass;
Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
// 父类即当前参数或方法所在的类
if (declaringClass == parentAsClass)
// 父类的实际参数列表
Type[] typeArgs = parentAsType.getActualTypeArguments();
// 父类上的泛型参数列表
TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();
for (int i = 0; i < declaredTypeVars.length; i++)
// 在父类的泛型参数上搜索与字段上用的泛型参数一致的参数
if (declaredTypeVars[i] == typeVar)
// 如果还是是泛型参数,继续解析
if (typeArgs[i] instanceof TypeVariable)
TypeVariable<?>[] typeParams = clazz.getTypeParameters();
for (int j = 0; j < typeParams.length; j++)
if (typeParams[j] == typeArgs[i])
if (srcType instanceof ParameterizedType)
result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
break;
else
// 如果不再是泛型参数,而是具体的某个类,直接返回这个类的 Type
// 如,父类的 List<T>,子类指定了 T 为 String,此时 typeArgs[i] = String
result = typeArgs[i];
else if (declaringClass.isAssignableFrom(parentAsClass))
// 如果当前类依然不是泛型参数所在的实际类,继续向父类查询
result = resolveTypeVar(typeVar, parentAsType, declaringClass);
else if (superclass instanceof Class)
// 声明的父类不含有类型变量且不是定义该字段的类,则继续解析
if (declaringClass.isAssignableFrom((Class<?>) superclass))
result = resolveTypeVar(typeVar, superclass, declaringClass);
return result;
上边的代码比较复杂,可以简单举例解释。
public class ClassA<T>
List<T> list;
public class ClassB extends ClassA<String>
- 解析ClassB 中的 list 字段时,对于 resolveType(fieldType, srcType, declaringClass) 方法:fieldType = List、srcType = ClassB、declaringClass = ClassA。
- 由于 List 是泛型参数类型 ParameterizedType,因此使用 resolveParameterizedType(parameterizedType, srcType, declaringClass) 方法,其中 parameterizedType = List、srcType = ClassB、declaringClass = ClassA。
- resolveParameterizedType 方法中的 typeArgs = T,正好为类型变量 TypeVariable,因此按照resolveTypeVar (typeArgs[i], srcType, declaringClass) 解析,此时 typeArgs[i] = T、srcType = ClassB、declaringClass = ClassA。
- resolveTypeVar 方法中,superclass = ClassA,进入 scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass) 方法,其中 typeVar = T、srcType = ClassB、declaringClass = ClassA、clazz = ClassB、superclass = ClassA。
- 在 scanSuperTypes 方法中,parentAsClass = declaringClass = ClassA,typeArgs = [String],declaredTypeVars = [T],因此 declaredTypeVars[0] = T && typeArgs != TypeVariable,因此结果即为 String。
- 最终在最外层的 resolveParameterizedType 方法里,从而得出 ParameterizedType(rawType = List、actualTypeArguments = [String])。
以上通过对方法字段的解析分析了 resolveParameterizedType 和 resolveTypeVar 方法,在 resolveType 方法中还包含第三个方法:resolveGenericArrayType。该方法负责解析 GenericArrayType 类型的变量,它会根据数组元素的类型选择合适的 resolve*() 方法进行解析。
private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass)
// 获取数组的元素类型(这里是在处理数组类型,即 List<String>[] 之类的数组)
Type componentType = genericArrayType.getGenericComponentType();
Type resolvedComponentType = null;
// 根据元素的类型进行解析
if (componentType instanceof TypeVariable)
// 类型变量,即原始对象为 T [] 这样的数组,其组件类型是 T
resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
else if (componentType instanceof GenericArrayType)
// 数组类型,即原始对象是二维数组 T[][],其组件类型还是 T[]
resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
else if (componentType instanceof ParameterizedType)
// 参数化类型,即原始对象为 List<T>[],其组件类型是 List<T>
resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
// 根据解析后的数组元素类型构造返回类型
// 如果是Class 则返回数组 Class;如果是其他类型,则返回封装对象
if (resolvedComponentType instanceof Class)
return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
else
return new GenericArrayTypeImpl(resolvedComponentType);
在 resolveParameterizedType 方法中还包含一个特殊的方法 resolveWildcardType 用于解析通配符类型的参数,即 WildcardType 类型。它首先解析类型的上下边界,然后将结果封装为 WildcardTypeImpl 对象并返回,其上下边界的解析和其他方法类似。
总结
通过 TypeParameterResolver 能够分析类型信息,它能够解析字段、方法参数、方法返回值的类型,并能够将其中的泛型信息解析到它的实际类型,而且它能够分析复杂继承情况,将父类的类型参数具体解析到子类中,获取其在子类中的实际类型。
参考文档:《Mybatis 技术内幕》
本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。
欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。
以上是关于Mybatis 源码学习-反射工具(TypeParameterResolver)的主要内容,如果未能解决你的问题,请参考以下文章
Mybatis 源码学习-反射工具(TypeParameterResolver)
Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)