Mybatis原码学习(四)TypeParameterResolver

得先申明下,这篇文章应该不会有借鉴价值,我也只能是斗胆总结下。
为什么呢,因为这是我目前为止遇到问题最大的一部分,绕过去绕过来,可能是自己对泛型、类型学习都还不够,理解得很吃力很吃力。
代码部分来自博客:https://www.jianshu.com/p/73bb429b9a01
我实在懒得打注释了,解释就用文章的方式吧。
在开始介绍TypeParmeterResolver之前,得了解一下Type的知识:
Type是所有类型的父接口,它有四个子接口和一个实现类。
下面来看这些子接口和子类所代表的类型。

  • Class 比较常见,它表示的是原始类型。Class 类的对象表示NM 中的一个类或接口,
    每个Java 类在NM 里都表现为一个Class 对象。在程序中可以通过“类名.class ”、“对
    象.getClass()”或是“Class.forName(‘类名’)”等方式获取class对象。数组也被映射成
    Class 对象,所有元素类型相同且维数相同的数组都共享同一个Class 对象。
  • ParameterizedType 表示的是参数化类型,例如List<String> 、Map<Integer,String>、
    Service<User>这种带有泛型的类型。
    ParameterizedType 接口中常用的方法有三个,分别是:
    1. Type getRawType ()一一返回参数化类型中的原始类型,例如List<String > 的原始类
      型为List 。
    2. Type[] getActualTypeArguments ()一一获取参数化类型的类型变量或是实际类型列
      表,例如Map<Integer, String> 的实际泛型列表Integer 和String 。需要注意的是,
      该列表的元素类型都是Type ,也就是说,可能存在多层嵌套的情况。
    3. Type getOwnerType ()一一返回是类型所属的类型,例如存在A类,其中定义了
      内部类InnerA<I> ,则InnerA<I> 所属的类型为A<T>,如果是顶层类型则返回null 。
      这种关系比较常见的示例是Map<K,V>接口与Map .Entry<K,V >接口, Map<K,V>
      接口是Map.Entry<K,V >接口的所有者。
  • TypeVariable 表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List
    它用来反映在JVM编译该泛型前的信息。例如List中的T就是类型变量,它在编译时需被转换为一个具体的类型后才能正常使用。
    该接口中常用的方法有三个,分别是:
    1. Type[] getBounds ()一一获取类型变量的上边界,如果未明确声明上边界则默认为
      Object 。例如class Test<K extends Person > 中K 的上界就是Person 。
    2. D getGenericDeclaration()一一获取声明该类型变量的原始类型,例如class Test<K extends Person>中的原始类型是Test 。
    3. String getName()一一获取在源码中定义时的名字,上例中为K 。
  • GenericArrayType 表示的是数组类型且组成元素是ParameterizedType 或TypeVariable .
    例如List<String>[]或T[] 。该接口只有Type getGenericComponentType () 一个方法,它返回数组的组成元素。
    WildcardType 表示的是通配符泛型,例如? extends Number 和? super Integer 。
    WildcardType 接口有两个方法,分别是:
    Type[] getUpperBounds ()一一-返回泛型变量的上界。
    Type[] getLowerBounds()一-返回泛型变量的下界。

上面这些就是我很不熟的部分,所以给整个学习过程带来了挺大的麻烦。
那么咱回到TypeParameterResolver的介绍,他是一个工具类,提供了一系列静态方法来解析指定类中的字段、方法返回值或方法参数的类型。

也就是说,它露给了我们三个静态方法,分别对应解析字段类型、方法参数、方法返回值的类型解析。这三个方法里面内容差不多,通过指定类型的方式去调用resolverType方法,然后resolverType方法根据传过来的字段,再调用后边的方法。比如resolveFieldType:

/**
 * 获取指定字段的实际类型,将泛型转换为实际类型
 *
 * @param field   字段
 * @param srcType 运行时字段所属对象类型
 * @return 如果field在声明的时候有泛型定义,这些泛型会被解析会运行时的实际类型
 */
public static Type resolveFieldType(Field field, Type srcType) {
    // 获取字段的类型
    Type fieldType = field.getGenericType();
    // 获取声明字段的类
    Class<?> declaringClass = field.getDeclaringClass();
    // 执行解析操作
    return resolveType(fieldType, srcType, declaringClass);
}

其中参数srcType指的是用于获取泛型实际类型的类/实际运行时的类/传参进来的类型所在的类,这是我看了很多博客后的总结。。反正也算理解那个意思了吧。
然后咱来到resolveType:

/**
 * 将指定类型中的泛型定义转换为运行时的实际类型定义
 *
 * @param type           可能包含泛型定义的类型
 * @param srcType        运行期间该类型所属的实例对象
 * @param declaringClass 声明了type定义的类
 * @return 指定类型处理完泛型定义后的类型
 */
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
    if (type instanceof TypeVariable) {
        // 解析类型参数,比如: 泛型参数表示具体的某一独立的类型
        return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
    } else if (type instanceof ParameterizedType) {
        // 解析参数化的泛型,比如:List<T> list;
        return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
    } else if (type instanceof GenericArrayType) {
        // 解析泛型数组,比如: List<N>[] listArray;
        return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
    } else {
        // 普通类型(Class)直接返回
        return type;
    }
}

resolveType的思路就是根据传参的type来确定接下来通过哪个方法来进行类型解析。

resolveTypeVar

我们进入第一个方法:resolveTypeVar,这个方法是用来解析TypeVariable的,也就是泛型,具体实现如下:

/**
 * 解析指定泛型变量的类型
 *
 * @param typeVar        泛型变量
 * @param srcType        用于获取泛型变量实际类型的类
 * @param declaringClass 实际声明该类型的类的类型
 */
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {

    Type result = null;
    Class<?> clazz = null;
    // step1: 处理srcType,移除泛型定义,获取对应的Class类型
    if (srcType instanceof Class) {
        clazz = (Class<?>) srcType;
    } else if (srcType instanceof ParameterizedType) {
        // 泛型参数取声明泛型的类型
        ParameterizedType parameterizedType = (ParameterizedType) srcType;
        clazz = (Class<?>) parameterizedType.getRawType();
    } else {
        // 理论上讲不会出现其他的类型场景
        throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
    }

    // step2: 获取泛型定义的实际类型

    // case1: 当前类就是声明了泛型的类
    if (clazz == declaringClass) {
        //  当前类就是声明了泛型的类,则泛型一定未被指定具体类型,获取泛型变量类型上限
        Type[] bounds = typeVar.getBounds();
        if (bounds.length > 0) {
            return bounds[0];
        }

        // 没有指定泛型变量上限,那就是Object。
        return Object.class;
    }

    // case2: 尝试从父类中获取泛型变量的实际类型

    // 运行期间泛型所属的对象和声明泛型定义的不是同一个
    // 获取其直接父类类型,尝试从父类中找到泛型定义
    Type superclass = clazz.getGenericSuperclass();
    // 递归处理父类,直到找到该泛型对应的实际类型,或者null值。
    result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
    if (result != null) {
        return result;
    }

    // case3: 无法通过父类获取泛型变量的实际类型,则通过接口定义获取泛型变量对应的实际类型

    // 获取类实现的所有接口,尝试从接口中获取泛型变量的定义
    Type[] superInterfaces = clazz.getGenericInterfaces();
    for (Type superInterface : superInterfaces) {
        result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
        if (result != null) {
            return result;
        }
    }
    // 在接口中也未找到泛型变量的实际类型,返回Object类型。
    return Object.class;
}

这个方法具体实现注释里面太明白了,我这里理下思路:

  1. 首先是参数,第一个参数typeVar是指我们要处理的泛型对象,第二个参数srcType和之前的一样,传参进来那个地方的类,第三个参数指的是字段定义地方的类。
  2. 然后判断传参进来的类是不是个class对象,也就是前面介绍的TYPE接口下面的Class。是的话那么ok,那么要处理的就是它了,于是直接把type实列化成一个类。如果他不是class对象,而是嵌套了ParameterizedType类型,比如List,那么我们把前面这个List拿出来,就是后边要处理的了。
  3. 然后就是类型的处理了,如果上一步拿下来的要处理的类正好是声明字段的类,那没办法了,里面的泛型咱肯定是处理不了的了,就往上找找吧,是在不行返回一个object。
  4. 如果上一步不满足,那么找到实际类型还有希望,于是尝试从父类中去找,这里调用scanSuperTypes实现去父类中找的操作
  5. 如果在父类中还找不到,再尝试去接口中找找。实在不行就返回Object。

scanSuperTypes

那么上面第4步中出现的scanSuperTypes咱也得分析一下:

private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
    Type result = null;

    /*
    * 判断处理的父类superclass是否为参数化类型,如果不是则代表declaringClass和superclass的基本Class
    * 类型不是同一个类。
    */
    if (superclass instanceof ParameterizedType) {
      //如果为ParameterizedType,则获取它基本类型
      ParameterizedType parentAsType = (ParameterizedType) superclass;
      Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();

      if (declaringClass == parentAsClass) {

        //如果declaringClass和parentAsClass表示同一类型,则通过typeVar在declaringClass的泛型形参的index获取其在supperClass中定义的类型实参
        Type[] typeArgs = parentAsType.getActualTypeArguments();
        TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();

        for (int i = 0; i < declaredTypeVars.length; i++) {
          //循环判断当前处理的类型是否属于所属的类型描述符中的变量
          if (declaredTypeVars[i] == typeVar) {

            /*
             *如果supperClass中定义的类型形参还是类型变量则取srcType中的类型形参的定义
             * 如果srcType中的类型形参还是类型变量则不处理。
             */
            if (typeArgs[i] instanceof TypeVariable) {
              //其子类中的所有泛型描述符
              TypeVariable<?>[] typeParams = clazz.getTypeParameters();
              for (int j = 0; j < typeParams.length; j++) {
                if (typeParams[j] == typeArgs[i]) {
                  //判断是否为ParameterizedType,则去实际代表的类型
                  if (srcType instanceof ParameterizedType) {
                    result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
                  }
                  break;
                }
              }
            } else {
              //如果不是TypeVariable,直接取对应的类型
              result = typeArgs[i];
            }
          }
        }
      } else if (declaringClass.isAssignableFrom(parentAsClass)) {
           //通过判断superclass是否是declaringClass的子类(由于java类可以实现多个接口),进行递归解析
        result = resolveTypeVar(typeVar, parentAsType, declaringClass);
      }
    } else if (superclass instanceof Class) {
      //如果superclass为Class类型,通过判断superclass是否是declaringClass的子类(由于java类可以实现多个接口),进行递归解析
      if (declaringClass.isAssignableFrom((Class<?>) superclass)) {
        result = resolveTypeVar(typeVar, superclass, declaringClass);
      }
    }

    return result;
  }

同样的,注释写的太明白了,我自己理一遍思路:

    1. 首先是参数,第一、二、三个参数和上一步是一样的,分别是第一个参数typeVar是指我们要处理的泛型对象,第二个参数srcType和之前的一样,传参进来那个地方的类,第三个参数指的是字段定义地方的类。
      第四个参数是从上一步的srcType分析出的待解析的类,第五个参数是带解析的类的父类(父接口)
    1. 首先判断第五个参数superclass是参数化类型,那么就取出它的原始类型typeArgs,比如List我们就拿出List。 如果这个typeArgs就是初始定义目标字段的类,那么就对头了。接下来拿出typeArgs的泛型参数比如{T},再拿出初始定义类的参数比如{k},判断这个k是不是我们要找的目标字段,是的话判断T是不是泛型了,然后就循环当前类clazz的参数列表,这里是看泛型在这里被参数化成了什么类型,如果当前类的某个参数类型和typeArgs里的参数类型相同,那就解析成功了。
    1. 如果superclass不是参数化类型,那就再往父类上递归找呗。

到这里resolveTypeVar就结束了,然后是resolveParameterizedType:

 private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
    //获取泛型的基本类型
    Class<?> rawType = (Class<?>) parameterizedType.getRawType();

    //获取泛型中的类型实参
    Type[] typeArgs = parameterizedType.getActualTypeArguments();

    //递归处理其类型实参
    Type[] args = new Type[typeArgs.length];
    for (int i = 0; i < typeArgs.length; i++) {
      //判断对应参数的类型,分别进行递归处理
      if (typeArgs[i] instanceof TypeVariable) {
        //解析TypeVariable类型
        args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof ParameterizedType) {
        //解析ParameterizedType类型
        args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof WildcardType) {
        //是解析WildcardType类型(其类型实参是通配符表达式)
        args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
      } else {
        //普通Class类型,直接返回
        args[i] = typeArgs[i];
      }
    }

    //返回自定以类型
    return new ParameterizedTypeImpl(rawType, null, args);
  }

resolveGenericArrayType和上面这个差不多,我也不再写了。。
到这里都还是昏昏的,过程大概有了个了解,但是和实列还是对不上,等后边学习到使用这个工具类部分的时候再理解吧。

个人博客:qlumen.cn

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值