MyBatis(技术NeiMu):基础支持层(类型转换模块与ObjectFactory)

回顾

前面我们已经看到了解析器模块和反射模块,下面来看一下类型转换模块

类型转换模块

TypeParameterResolver

TypeParameterResolver是一个工具类,负责提供一系列静态方法来解析指定类中的字段、方法返回值或方法参数的类型

可能有人觉得为什么要进行解析呢?类型直接通过反射不就可以获取了吗?

的确是这样,对于一般类型来说,使用反射就可以获取了,但如果是对于泛型呢?你压根不知道泛型是什么类型?此时就要去进行解析了
在这里插入图片描述
在这里插入图片描述
主要的方法有三个

  • resolveFieldType:解析字段类型的
  • resolverParamTypes:解析方法参数列表中各个参数的类型
  • resolverReturnType:解析方法的返回值类型的
解析字段类型的——resolveFieldType

源码如下

public static Type resolveFieldType(Field field, Type srcType) {
    //获取字段的声明类型,也就是字段的数据类型
    Type fieldType = field.getGenericType();
    //获取字段所在的类的Class对象
    Class<?> declaringClass = field.getDeclaringClass();
    //调用resolveType方法
    return resolveType(fieldType, srcType, declaringClass);
  }

可以看到,对于字段类型的处理主要是交由resolveType方法来处理的

解析方法参数列表中各个参数的类型——resolverParamTypes

源码如下

public static Type[] resolveParamTypes(Method method, Type srcType) {
    //获取方法里面的参数列表
    Type[] paramTypes = method.getGenericParameterTypes();
    //获取方法定义所在的类
    Class<?> declaringClass = method.getDeclaringClass();
    //结果集
    Type[] result = new Type[paramTypes.length];
    //遍历每一个参数
    for (int i = 0; i < paramTypes.length; i++) {
        //每一个参数都使用resolveType进行处理
        //将处理结果存放到结果集中
      result[i] = resolveType(paramTypes[i], srcType, declaringClass);
    }
    //返回结果集
    return result;
  }

可以看到,对于方法参数列中各个参数的类型,也是交由resolveType方法来处理的

解析方法返回值的类型——resolverReturnType

源码如下

public static Type resolveReturnType(Method method, Type srcType) {
    //获取方法的返回值类型
    Type returnType = method.getGenericReturnType();
    //获取方法定义所在的类的Class对象
    Class<?> declaringClass = method.getDeclaringClass();
    //使用resolveType来解析
    return resolveType(returnType, srcType, declaringClass);
  }

可以看到,对于解析返回值类型的,也是交由resolveType方法来解析的!!!

从上面可以知道,无论是字段、方法的参数列表还是方法的返回值,都是交由resolveType方法来处理的,并且这里为什么要进行解析呢?直接反射不就能获取类型了吗?

resolveType

下面就来看看这个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 {
        //不是上面三种类型的,直接返回Type
      return type;
    }
  }

可以看到,resolveType采用了一个简单的工厂模式,调用不同的方法去解析各种Type,所以首先我们得认识Type接口

Type接口

在这里插入图片描述
可以看到,这是一个JDK1.5出现的接口(用来表明类型的),并且有4个接口继承它,Class类实现了Type接口

  • ParameterizedType:表示的是参数化的类型,既带有泛型的类型,比如List、Map,因为这种通常带有多种类型
    • getRawType:返回参数化类型中的原始类型,比如List,返回的就是List
    • getActualTypeArguments:获取参数化类型的类型变量列表,既相当于获取里面的泛型,比如Map<Integer,String>获取的列表就是Integer和String
    • getOwnerType:返回类型所属的类型,这个是针对内部类的,可以根据内部类去获取外部类的Type,如果本身就是最外部类,获取的就为Null
  • TypeVariable:表示的是类型变量,用来反映JVM编译该泛型前的信息(要注意,这是泛型前的信息,是不知道后续使用时的具体信息的!!!!)
    • getBounds:获取类型变量的上边界,也就是父类,如果没有声明父类那就是为Object,比如Test,那么T的上边界就是Person
    • getGenericDeclaration:获取声明该类型变量的原始类型,比如List,那么获取的就是List
    • getName:获取类型变量在源码中的名字,比如T、K这些
  • GenericArrayType:表示的是数组类型并且组成元素的类型是Parameterized或者TypeVariable的
    • getGenericComponentType:返回数组中的组成元素
  • WildcardType:通配符类型

现在回到我们的resolveType

在这里插入图片描述
可以看到,其仅仅对于下面三种类型做特殊处理

  • TypeVariable:泛型
  • ParameterizedType:带有泛型的类型
  • GenericArrayType:数组类型,并且数组里面是泛型或者带有泛型的类型

对于其他的类型,说白就是与泛型无关的类型,都是直接返回

下面就来看看,是如何对泛型进行处理的

首先我们要理清楚三个参数

  • type:字段、方法参数、方法的返回值的Type
  • declaringClass:字段所属类的类型、方法所属类的类型(Class类型)
  • srcType:来源Type,也就是说从哪个Type里面进行解析,其代表的就是要进行解析的类
resolverParameterizedType

先来看看如何对带有泛型的类型来进行处理的

源码如下

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++) {
       //如果泛型类型是TypeVariable类型的
      if (typeArgs[i] instanceof TypeVariable) {
          //交由resolveTypeVar方法负责处理
        args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof ParameterizedType) {
          //如果泛型类型又是ParameterizedType的,既又是一个带有泛型的类型
          //交由resolveParameterizedType处理
        args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof WildcardType) {
          //如果是wildcardType
          //交由resolveWildcardType方法处理
        args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
      } else {
          //都不是就不进行处理了
        args[i] = typeArgs[i];
      }
    }
    //返回ParameterizedTypeImpl
    return new ParameterizedTypeImpl(rawType, null, args);
  }

可以看到,对于ParameterizedType类型是对类型变量里面的泛型类型列表继续进行拆分的,因为带有类型变量的

resolverTypeVar

再来看看如何解析TypeVariable的

该方法源码如下

private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
    Type result;
    //clazz存储srcType自身的Class类型
    Class<?> clazz;
    
    //判断srcType是不是Class类型
    //如果是Class类型,那就不带有泛型列表了
    if (srcType instanceof Class) {
        //直接强转成Class类型
      clazz = (Class<?>) srcType;
    } 
    //如果srcType是ParameterizedType
    //证明srcType是一个类型变量
    else if (srcType instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) srcType;
        //获取类型变量的原始类型
      clazz = (Class<?>) parameterizedType.getRawType();
    } 
    //其他类型就抛错
    //所以srcType这里只能是Class或者ParameterizedType
    else {
      throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
    }
	//如果srcType的类型等于declaringClass(声明的类型)
    //这里是判断有没有嵌套了
    //如果clazz == declaringClass
    //代表当前要解析的字段、参数、返回值其实就定义在当前类上
    if (clazz == declaringClass) {
        //如果就位于当前类上,而typeVar是来自于字段的
        //这就说明了字段是来自于当前类的,
        //获取要解析的泛型的上界
      Type[] bounds = typeVar.getBounds();
        //如果有上界
      if (bounds.length > 0) {
          //返回第一个,既第一个父类
          //此时就确认了泛型的类型了!!!!!
          //类型是跟随第一个父类的
        return bounds[0];
      }
        //如果没有上界,返回Object类型
        //代表该泛型为Object.class类型????
        //为什么会返回Object.class?
        //如果判断是在当前类的化,不应该是要从当前类中比较筛选吗?
      return Object.class;
    }
	//如果不是在当前类上,则需要去寻找父类
    Type superclass = clazz.getGenericSuperclass();
    //从父类中进行解析
    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;
  }

可以看到,其实所谓的解析字段、解析方法参数、解析方法返回值,其实就是去弄清楚这个泛型代表的是什么类型!

首先要解决第一件事,找到这个字段、方法参数或者方法返回值位于的解析类中的哪里?是位于当前解析类上了,还是解析类的父类上了?

  • 判断当前解析类的类型
    • 如果是Class类型,直接转化为Class
    • 如果是ParameterizedType类型,则要转化为RawType(既原始类型)
  • 然后通过比较解析类的类型与字段、方法参数或者方法返回值位于的类的类型是否一致
    • 如果一致,代表就要从字段、方法参数或方法返回值的类型就位于当前解析类中
      • 因为是位于当前类中,所以直接Type
    • 如果不一致,则还要继续往下找
      • 先递归找父类
      • 最后找接口
  • 如果最后都没有,返回Object类型,代表要解析的字段、方法参数或方法返回值为Object类型

下面就来看看是怎么递归找父类的,对应的方法为scanSuperTypes

该方法源码如下

private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
    //如果父类也是ParameterizedType类型
    if (superclass instanceof ParameterizedType) {
      ParameterizedType parentAsType = (ParameterizedType) superclass;
        //获取父类的变量类型的原始类型
      Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
        //获取泛型列表
      TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
        //判断要解析的类是不是ParameterizedType
      if (srcType instanceof ParameterizedType) {
          //如果是,要进行translateParentTypeVars方法解析
        parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
      }
        //如果此时父类是泛型所在类
      if (declaringClass == parentAsClass) {
          //遍历父类的泛型列表
        for (int i = 0; i < parentTypeVars.length; i++) {
            //找到对应的泛型
          if (typeVar.equals(parentTypeVars[i])) {
              //从泛型类型列表里面面返回对应的类型
            return parentAsType.getActualTypeArguments()[i];
          }
        }
      }
        //如果泛型所在类是父类的父类
      if (declaringClass.isAssignableFrom(parentAsClass)) {
          //递归执行resolveTypeVar,继续找下一个父类
        return resolveTypeVar(typeVar, parentAsType, declaringClass);
      }
    }
    //如果父类是一个Class类型,并且泛型所在的类是父类的父类
    //为Class类型就证明没有类型变量了,也就是没有泛型类型列表
    else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
        //仍然递归执行resolveTypeVar去再往下寻找
      return resolveTypeVar(typeVar, superclass, declaringClass);
    }
    return null;
  }

//泛型解析看的裂开。。。。。。

ObjectFactory

MyBatis通常使用ObjectFactory来创建指定类型的对象,并且调用的是里面的create方法

在这里插入图片描述
在这里插入图片描述
可以看到ObjectFactory提供了两种create方式

  • 根据Class对象进行Create,猜测应该是无参构造
  • 根据Class对象、指定的参数类型、对应参数类型的值来进行Create,说白了就是指定使用哪个构造器来create,通过参数类型顺序来限制

在这里插入图片描述
而且可以看到,ObjectFactory只有DefaultObjectFactory一个实现类

下面就来看看是如何创建的

在这里插入图片描述

create(Class type)

源码如下

public <T> T create(Class<T> type) {
    return create(type, null, null);
  }

可以看到,其直接调用了重载的指定构造器的方法,并且传的参数类型顺序,类型顺序对应的值为Null而已,所以我们只需要看懂如何根据指定参数类型顺序来创建对象的方法即可

create(Class type,List…)

源码如下

public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    //对接口类型的Class进行处理
    //为什么要处理接口类型呢?之前我们在dao文件里面去接收一个List集合的时候
    //我们给的方法返回值仅仅只是一个List接口,而当查询完数据库,返回数据时,却发现变成了一个ArrayList
    //而这里就是做了这一层的处理
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    //调用instantiateClass来创建对象
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }
处理集合接口

resolveInterface是处理集合理性的接口的

protected Class<?> resolveInterface(Class<?> type) {
    Class<?> classToCreate;
    //如果是List接口、Collection接口、Iterable接口
    if (type == List.class || type == Collection.class || type == Iterable.class) {
        //默认返回一个ArrayList
      classToCreate = ArrayList.class;
    } 
    //如果是Map接口
    else if (type == Map.class) {
        //返回HashMap
      classToCreate = HashMap.class;
    } 
    //如果是SortedSet接口
    else if (type == SortedSet.class) { 
        //返回TreeSet
      classToCreate = TreeSet.class;
    }
    //如果是Set接口
    else if (type == Set.class) {
        //返回一个HashSet
      classToCreate = HashSet.class;
    } 
    //其他类型的就不做处理了
    else {
      classToCreate = type;
    }
    return classToCreate;
  }

规则如下

  • List、Collection、Iterable接口,默认就是ArrayList
  • Map接口默认是HashMap
  • SortedSet接口默认是TreeSet
  • Set接口默认是HashSet
  • 其他接口不做处理
确定好类型后创建对象

对应的方法源码如下

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
        //如果传进来的构造参数为Null
      if (constructorArgTypes == null || constructorArgs == null) {
          //获取无参构造器
        constructor = type.getDeclaredConstructor();
        try {
            //使用构造器来创建对象
          return constructor.newInstance();
        } catch (IllegalAccessException e) {
            //捕捉构造器为不可以访问的错误
            //Reflector决定是不是可以控制开放权限
          if (Reflector.canControlMemberAccessible()) {
              //修改为可访问
              //继续创建
            constructor.setAccessible(true);
            return constructor.newInstance();
          } else {
            throw e;
          }
        }
      }
        //如果构造参数、构造类型顺序不为空
        //根据传来的构造参数类型顺序找到构造器
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
      try {
          //使用对应的构造参数来进行创建对象
        return constructor.newInstance(constructorArgs.toArray(new Object[0]));
      } catch (IllegalAccessException e) {
          //同样处理
        if (Reflector.canControlMemberAccessible()) {
          constructor.setAccessible(true);
          return constructor.newInstance(constructorArgs.toArray(new Object[0]));
        } else {
          throw e;
        }
      }
    } catch (Exception e) {
      String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
          .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
      String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
          .stream().map(String::valueOf).collect(Collectors.joining(","));
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

可以看到,逻辑非常简单,直接利用反射,根据构造参数的类型顺序来获取构造器,反射来创建对象而已

当然,我们也可以自定义我们的ObjectFactory实现类

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值