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 源码学习-反射工具(Reflector)

Mybatis 源码学习-反射工具(MetaClass)

Mybatis 源码学习-反射工具(TypeParameterResolver)

Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Mybatis 源码学习-类型转换(TypeHandler)