MyBatis源码实现之反射工具箱TypeParameterResolver

小灰灰 2022-04-24 09:38 297阅读 0赞

反射工具箱之TypeParameterResolver

Type

在开始介绍TypeParameterResolver 之前,先简单介绍一下Type接口的基础知识。Type 是
所有类型的父接口,它有四个子接口和一个实现类,如图2-10 所示。

在这里插入图片描述

下面来看这些子接口和子类所代表的类型。

  • Class 比较常见,它表示的是原始类型。Class 类的对象表示NM 中的一个类或接口,
    每个Java 类在NM 里都表现为一个Class 对象。在程序中可以通过“类名.class ”、“对
    象.getClass()”或是“Class.forName(‘类名’)”等方式获取class对象。数组也被映射成
    Class 对象,所有元素类型相同且维数相同的数组都共享同一个Class 对象。
  • ParameterizedType 表示的是参数化类型,例如List<String> 、Map<Integer,String>、
    Service<User>这种带有泛型的类型。
    ParameterizedType 接口中常用的方法有三个,分别是:

    • Type getRawType ()一一返回参数化类型中的原始类型,例如List<String > 的原始类
      型为List 。
    • Type[] getActualTypeArguments ()一一获取参数化类型的类型变量或是实际类型列
      表,例如Map<Integer, String> 的实际泛型列表Integer 和String 。需要注意的是,
      该列表的元素类型都是Type ,也就是说,可能存在多层嵌套的情况。
    • Type getOwnerType ()一一返回是类型所属的类型,例如存在A类,其中定义了
      内部类InnerA
      接口是Map.Entry<K,V >接口的所有者。
  • TypeVariable 表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List
    中的T就是类型变量,它在编译时需被转换为一个具体的类型后才能正常使用。
    该接口中常用的方法有三个,分别是:

    • Type[] getBounds ()一一获取类型变量的上边界,如果未明确声明上边界则默认为
      Object 。例如class Test<K extends Person > 中K 的上界就是Person 。
    • D getGenericDeclaration()一一获取声明该类型变量的原始类型,例如class Test<K
      extends Person>中的原始类型是Test 。
    • String getNameO 一一获取在源码中定义时的名字,上例中为K 。
  • GenericArrayType 表示的是数组类型且组成元素是ParameterizedType 或TypeVariable .
    例如List<String>[]或T[] 。该接口只有Type getGenericComponentType () 一个方法,它
    返回数组的组成元素。
  • WildcardType 表示的是通配符泛型,例如? extends Number 和? super Integer 。
    WildcardType 接口有两个方法,分别是:

    • Type[] getUpperBounds ()一一-返回泛型变量的上界。
    • Type[] getLowerBounds()一-返回泛型变量的下界。

TypeParameterResolver

介绍完Type接口的基础知识,我们回到对TypeParameterResolver 介绍。在对Reflector的
分析过程中,我们看到了TypeParameterResolver 的身影,它是一个工具类,提供了一系列静态
方法来解析指定类中的宇段、方法返回值或方法参数的类型。TypeParameterResolver 中各个静
态方法之间的调用关系大致如图2-11 所示,为保持清晰,其中递归调用没有表现出来,在后面
的代码分析过程中会进行强调。

在这里插入图片描述

TypeParameterResolver 中通过resolveFieldType ()方法、resolveReturnType () 方法、
resolveParamTypes ()方法分别解析宇段类型、方法返回值类型和方法参数列表中各个参数的类型。
这三个方法的逻辑基本类似,这里以resolveFieldType ()方法为例进行介绍,剩余两个方法请读
者参考源码学习。TypeParameterResolver.resolveFieldType ()方法的具体实现如下:

  1. public static Type resolveFieldType(Field field, Type srcType) {
  2. // 获取字段的声明类型
  3. Type fieldType = field.getGenericType();
  4. // 获取字段定义所在类的Class对象
  5. Class<?> declaringClass = field.getDeclaringClass();
  6. // 调用resolveType进行后续处理
  7. return resolveType(fieldType, srcType, declaringClass);
  8. }

注:

getType(): 获取属性声明时类型对象(返回class对象)
getGenericType() : 返回属性声的Type类型
getType() 和 getGenericType()的区别 :
1.首先是返回的类型不一样,一个是Class对象一个是Type接口。
2.如果属性是一个泛型,从getType()只能得到这个属性的接口类型。但从getGenericType()还能得到这个泛型的参数类型。
3.getGenericType()如果当前属性有签名属性类型就返回,否则就返回 Field.getType()。

如图2-11所示,上述三个方法都会调用resolveType() 方法,该方法会根据其第一个参数的
类型,即字段、方法返回值或方法参数的类型,选择合适的方法进行解析。resolveType()方法的
第二个参数表示查找该字段、返回值或方法参数的起始位置。第三个参数则表示该字段、方法
定义所在的类。TypeParameterResolver.resolveType()方法代码如下:

  1. private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
  2. if (type instanceof TypeVariable) {
  3. // 解析TypeVariable类型(JVM编译该泛型前的信息。例如List<T>)
  4. return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
  5. } else if (type instanceof ParameterizedType) {
  6. // 解析ParameterizedType类型(表示的是参数化类型,例如List<String>)
  7. return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
  8. } else if (type instanceof GenericArrayType) {
  9. // 解析GenericArrayType(数组类型且组成元素是ParameterizedType 或TypeVariable .
  10. // 例如List<String>[]或T[] )
  11. return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
  12. } else {
  13. // Class类型
  14. return type;
  15. }
  16. // 字段、返回值、参数不可能直接定义成WildcardType 类型,但可以嵌套在别的类型中,后面会分析到
  17. }

为了读者便于理解,这里通过一个示例分析resolveType()方法,假设有三个类一-ClassA 、
SubClassA 、TestType ,代码如下:

  1. import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;
  2. import org.apache.ibatis.reflection.TypeParameterResolver;
  3. import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
  4. import java.lang.reflect.Field;
  5. import java.lang.reflect.ParameterizedType;
  6. import java.lang.reflect.Type;
  7. import java.util.HashMap;
  8. class classA<K, V> {
  9. protected HashMap<K, V> map;
  10. public HashMap<K, V> getMap() {
  11. return map;
  12. }
  13. public void setMap(HashMap<K, V> map) {
  14. this.map = map;
  15. }
  16. }
  17. class SubClassA<T> extends classA<T, T>{}
  18. class TestType {
  19. SubClassA<Long> aa = new SubClassA<>();
  20. public static void main(String[] args) throws Exception {
  21. Field field = classA.class.getDeclaredField("map");
  22. System.out.println(field.getGenericType());
  23. System.out.println(field.getGenericType() instanceof ParameterizedType);
  24. System.out.println("-----------------------------");
  25. // 解析SubA<Long> ( ParameterizedType 类型)中的map 字段,注意: ParameterizedTypeimpl 是
  26. // 在sun.reflect.generics.reflectObjects包下的ParameterizedType 接口实现
  27. Type type = TypeParameterResolver.resolveFieldType(field, ParameterizedTypeImpl.make(SubClassA.class, new Type[]{Long.class}, TestType.class));
  28. System.out.println(type.getClass());
  29. System.out.println(type);
  30. // 输出:class org.apache.ibatis.reflection.TypeParameterResolver$ParameterizedTypeImpl
  31. // 注意, TypeParameterResolver$ParameterizedTypeImpl是ParameterizedType 接口的实现
  32. // ~~~
  33. // 也可以使用下面的方式生成上述ParameterizedType 对象,
  34. // 并调用TypeParameterResolver.resolveFieldType ()方法:
  35. // Type aaa = TestType.class.getDeclaredField("aa").getGenericType();
  36. // // com.gyoomi.parameterresolver.SubClassA<java.lang.Long>
  37. // Type type2 = TypeParameterResolver.resolveFieldType(field, TestType.class.getDeclaredField("aa").getGenericType());
  38. // System.out.println(type2);
  39. // ~~~
  40. ParameterizedType pType = (ParameterizedType) type;
  41. System.out.println(pType);
  42. System.out.println("----------------------------");
  43. System.out.println("rawType:" + pType.getRawType());
  44. System.out.println("ownerType:" + pType.getOwnerType());
  45. Type[] actualTypeArguments = pType.getActualTypeArguments();
  46. for (Type actualTypeArgument : actualTypeArguments) {
  47. System.out.println(actualTypeArgument);
  48. }
  49. }
  50. }

根据前面对Type 接口的介绍,上例中ClassA.map 字段声明的类型Map<K,V>是
ParameterizedType类型,resolveType()方法会调用resolveParameterizedType()方法进行解析。首
先介绍resolveParameterizedType()方法的参数: 第一个参数是待解析的ParameterizedType 类型;
第二个参数是解析操作的起始类型:第三个参数为定义该字段或方法的类的Class 对象。在该
示例中第一个参数是Map<K,V>对应的ParameterizedType 对象,第二个参数是
TypeText.SubA <Long>对应的ParameterizedType 对象,第三个参数是ClassA (声明map 字段的
类)相应的Class 对象。TypeParameterResolver.resolveParameterizedType ()方法代码如下:

  1. private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
  2. // 在该示例中,得到的原始类型class对象
  3. Class<?> rawType = (Class<?>) parameterizedType.getRawType();
  4. // 类型变量为K、V
  5. Type[] typeArgs = parameterizedType.getActualTypeArguments();
  6. // 用来保存解析后的结果
  7. Type[] args = new Type[typeArgs.length];
  8. // 循环解析K, V
  9. for (int i = 0; i < typeArgs.length; i++) {
  10. if (typeArgs[i] instanceof TypeVariable) { // 类型变量,JVM编译前的类型
  11. args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
  12. } else if (typeArgs[i] instanceof ParameterizedType) { // 泛型化的参数类型
  13. // 如果嵌套了ParameterizedType ,则调用resolveParameterizedType ()方法进行处理
  14. args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
  15. } else if (typeArgs[i] instanceof WildcardType) { // 通配符的参数类型
  16. // 如果嵌套了WildcardType ,则调用resolveWildcardType()方法进行处理
  17. args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
  18. } else { // Class类型
  19. args[i] = typeArgs[i];
  20. }
  21. }
  22. // TypeParameterResolver.ParameterizedTypeImpl是当前类一个内部类
  23. // 将解析结果封装成TypeParameterResolver 中定义的Parameter工zed Type 实现并返回,本例中args
  24. // 数组中的元素都是Long.class
  25. return new ParameterizedTypeImpl(rawType, null, args);
  26. }

TypeParameterResolver.resolveTypeVar()方法负责解析TypeVariable ,本例会调用该方法解析
SubClassA.map字段的K和V。在该示例中,第一个参数是类型变量K 对应的TypeVariable对
象,第二个参数依然是TypeText.SubA<Long>对应的ParameterizedType对象,第三个参数是
ClassA(声明map 字段的类)对应的Class对象。TypeParameterResolver.resolveTypeVar()方法的
具体实现如下:

  1. private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
  2. Type result = null;
  3. Class<?> clazz = null;
  4. if (srcType instanceof Class) {
  5. clazz = (Class<?>) srcType;
  6. // 本例中SubA<Long>是ParameterizedType类型,clazz为SubClassA对应的Class对象
  7. } else if (srcType instanceof ParameterizedType) {
  8. ParameterizedType parameterizedType = (ParameterizedType) srcType;
  9. clazz = (Class<?>) parameterizedType.getRawType();
  10. } else {
  11. throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
  12. }
  13. // 因为SubClassA 继承了ClassA且map 字段定义在ClassA 中,故这里的srcType与declaringClass
  14. // 并不相等。如果map 字段定义在SubClassA中,则可以直接结束对K的解析
  15. if (clazz == declaringClass) {
  16. Type[] bounds = typeVar.getBounds();
  17. if(bounds.length > 0) {
  18. return bounds[0];
  19. }
  20. return Object.class;
  21. }
  22. // 获取声明的父类类型,即ClassA<T,T >对应的ParameterizedType对象
  23. Type superclass = clazz.getGenericSuperclass();
  24. // 通过扫描父类进行后续解析,这是递归的入口
  25. result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
  26. if (result != null) {
  27. return result;
  28. }
  29. // 获取接口
  30. Type[] superInterfaces = clazz.getGenericInterfaces();
  31. for (Type superInterface : superInterfaces) {
  32. // 通过扫描接口进行后续解析,逻辑同扫描父类(略)
  33. result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
  34. if (result != null) {
  35. return result;
  36. }
  37. }
  38. // 若在整个继承结构中都没有解析成功,则返回Object.class
  39. return Object.class;
  40. }

我们继续分析scanSuperTypes()方法,该方法会递归整个继承结构井完成类型变量的解析。
在该示例之中,第一个参数是K对应的TypeVariable 对象,第二个参数是TypeText.SubA
对应的ParameterizedType 对象,第三个参数是ClassA(声明map字段的类)对应的Class 对象,
第四个参数是SubClassA 对应的Class 对象,第五个参数是Class 对应的ParameterizedType
对象。scanSuperTypes ()方法的具体实现如下:

  1. private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
  2. Type result = null;
  3. // superclass 是ClassA<T ,T >对应的ParameterizedType 对象,条件成立
  4. if (superclass instanceof ParameterizedType) {
  5. ParameterizedType parentAsType = (ParameterizedType) superclass;
  6. // 原始类型是classA
  7. Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
  8. // map字段定义在classA类中条件成立
  9. if (declaringClass == parentAsClass) {
  10. // {T, T}
  11. Type[] typeArgs = parentAsType.getActualTypeArguments();
  12. // 在classA中定义的类型变量K, V
  13. TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();
  14. for (int i = 0; i < declaredTypeVars.length; i++) {
  15. // 解析的目标类型是K
  16. if (declaredTypeVars[i] == typeVar) {
  17. if (typeArgs[i] instanceof TypeVariable) { // T是类型变量,条件成立
  18. // SubClassA 只有一个类型交量T,且声明的父类是ClassA<T ,T >,本例中T被参数化为Long,则K参数化为Long
  19. TypeVariable<?>[] typeParams = clazz.getTypeParameters();
  20. for (int j = 0; j < typeParams.length; j++) {
  21. if (typeParams[j] == typeArgs[i]) {
  22. if (srcType instanceof ParameterizedType) {
  23. result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
  24. }
  25. break;
  26. }
  27. }
  28. } else {
  29. // 如果SubClassA 继承了ClassA<Long, Long >,则typeArgs[i]不是TypeVariable 类型,直接返回Long.class
  30. result = typeArgs[i];
  31. }
  32. }
  33. }
  34. } else if (declaringClass.isAssignableFrom(parentAsClass)) { // 继续解析父类,直到解析到定义该字段的类
  35. result = resolveTypeVar(typeVar, parentAsType, declaringClass);
  36. }
  37. } else if (superclass instanceof Class) { // 声明的父类不再含有类型变量且不是定义该字段的类,则继续解析
  38. if (declaringClass.isAssignableFrom((Class<?>) superclass)) {
  39. result = resolveTypeVar(typeVar, superclass, declaringClass);
  40. }
  41. }
  42. return result;
  43. }

为了便于读者理解scanSuperTypes ()方法的功能,图2-12 展示了scanSuperTypes()方法解析
类型变量的核心逻辑。

在这里插入图片描述

介绍完TypeParameterResolver.resolveTypeVar()和resolveParameterizedType() 两个方法之后,
再来看resolveGenericArrayType()方法,该方法负责解析GenericArrayType 类型的变量,它会根
据数组元素的类型选择合适的resolve *()方法进行解析,具体实现如下:

  1. private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass) {
  2. // 获取数纽元素的类型
  3. Type componentType = genericArrayType.getGenericComponentType();
  4. Type resolvedComponentType = null;
  5. if (componentType instanceof TypeVariable) {
  6. // resolveTypeVar()方法已经介绍过了,不再赘述
  7. resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
  8. } else if (componentType instanceof GenericArrayType) {
  9. // 逆归调用resolveGenericArrayType() 方法
  10. resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
  11. } else if (componentType instanceof ParameterizedType) {
  12. // resolveParameterizedType ()方法已经介绍过了,不再赘述
  13. resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
  14. }
  15. // 根据解析后的数组项类型构造返回类型
  16. if (resolvedComponentType instanceof Class) {
  17. return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
  18. } else {
  19. return new GenericArrayTypeImpl(resolvedComponentType);
  20. }
  21. }

最后我们来看一下TypeParameterResolver.resolveWildcardType ()方法,该方法负责解析
WildcardType 类型的变量。它首先解析WildcardType 中记录的上下界,然后通过解析后的结果
构造WildcardTypeImpl对象返回。具体解析过程与上述resolve*()方法类似,不再贴出代码了。

通过前面的分析可知,当存在复杂的继承关系以及泛型定义时,TypeParameterResolver 可
以帮助我们解析字段、方法参数或方法返回值的类型,这是前面介绍的Reflector类的基础。

另外,MyBatis 源代码中提供了TypeParameterResolverTest 这个测试类,其中从更多角度测
试了TypeParameterResolver的功能,感兴趣的读者可以参考该测试类的实现,可以更全面地了
解TypeParameterResolver的功能。

发表评论

表情:
评论列表 (有 0 条评论,297人围观)

还没有评论,来说两句吧...

相关阅读