mybatis 源码分析四、反射

mybatis 源码分析四、反射

一、mybatis反射基础Reflector类

mybatis中反射的应用非常多,在参数和结果集映射的过程很多地方都用到了反射。为了程序的可读性,简化反射的应用,mybatis针对对jdk自带的反射包进行了封装。接下来我们就看看mybatis是如何对反射进行封装的。

在mybatis 源码中我们反射操作都集中在org.apache.ibatis.reflection 这个包下面。我们看一下大概的包结构。

在这里插入图片描述

其中Reflector类是mybatis的基础,里面包含了很多信息,接下来我们看一下Reflector这个类的属性信息

  // 对应的Class 类型 
  private final Class<?> type;
  // 可读属性的名称集合 可读属性就是存在 getter方法的属性
  private final String[] readablePropertyNames;
  // 可写属性的名称集合 可写属性就是存在 setter方法的属性
  private final String[] writablePropertyNames;
  // 记录了属性相应的setter方法,key是属性名称,value是Invoker方法
  // 他是对setter方法对应Method对象的封装
  private final Map<String, Invoker> setMethods = new HashMap<>();
  // 属性相应的getter方法
  private final Map<String, Invoker> getMethods = new HashMap<>();
  // 记录了相应setter方法的参数类型,key是属性名称 value是setter方法的参数类型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  // 和上面的对应
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  // 记录了默认的构造方法
  private Constructor<?> defaultConstructor;

  // 记录了所有属性名称的集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

我们初始化这些信息都是在Reflector的构造器中进行的。

  public Reflector(Class<?> clazz) {
  	//初始化type
    type = clazz;
    //初始化默认的构造器
    addDefaultConstructor(clazz);
    //初始化属性的get方法
    addGetMethods(clazz);
    //初始话属性的set方法
    addSetMethods(clazz);
    //初始话字段集合
    addFields(clazz);
   //getter方法的属性 可读属性
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    //setter方法的属性 可写属性
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    //记录所有属性的集合
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

mybatis 提供了反射工厂用于加载Reflector,提供了一个ReflectorFactory工厂接口,和两个实现类一个是DefaultReflectorFactory 默认的实现,CustomReflectorFactory不过这个目前还没有什么实现。我们看一下DefaultReflectorFactory的源码。这里其实就是用来获取Reflector反射信息。

public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
      //用Map进行缓存
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      //获取反射的信息
      return new Reflector(type);
    }
  }

}

二、addDefaultConstructor如何获取默认构造器

通过源码我们可以看出其是将无参构造器设置为默认的构造器

  private void addDefaultConstructor(Class<?> clazz) {
    //获取这个类所有的构造器
    Constructor<?>[] consts = clazz.getDeclaredConstructors();
    for (Constructor<?> constructor : consts) {
      //找无参构造
      if (constructor.getParameterTypes().length == 0) {
        //判断是否有权限
        if (canAccessPrivateMethods()) {
          try {
            //破坏封装private修饰的
            constructor.setAccessible(true);
          } catch (Exception e) {
            // Ignored. This is only a final precaution, nothing we can do.
          }
        }
        //无参构造为默认的构造器
        if (constructor.isAccessible()) {
          this.defaultConstructor = constructor;
        }
      }
    }
  }

三、addGetMethods如何获取的get方法

  1. 主方法 addGetMethods解析,匹配符合添加的get方法

  /**
   * 添加get方法
   * 安装javaBean规范 get方法可以为get开头boolean类型
   * 的方法可以以is开头
   * @param cls class
   */
  private void addGetMethods(Class<?> cls) {
    //保存满足条件的get方法 ,以方法名称为key,
    Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
    //获取所有的方法
    Method[] methods = getClassMethods(cls);
    for (Method method : methods) {
      //排除有参构造
      if (method.getParameterTypes().length > 0) {
        continue;
      }
      //获取方法名称判断是否是以get 或者 is开头
      String name = method.getName();
      if ((name.startsWith("get") && name.length() > 3)
          || (name.startsWith("is") && name.length() > 2)) {
        //解析方法名称,比如 getName 会解析为 Name
        name = PropertyNamer.methodToProperty(name);
        //将相同key的方法放到value集合
        addMethodConflict(conflictingGetters, name, method);
      }
    }
    //解析合法的get方法
    resolveGetterConflicts(conflictingGetters);
  }
  1. getClassMethods获取所有的方法,是递归进行获取的
  private Method[] getClassMethods(Class<?> cls) {
    //保存所有的方法和接口包含父类和接口的
    Map<String, Method> uniqueMethods = new HashMap<String, Method>();
    Class<?> currentClass = cls;
    //终止条件 单前类是object或者为null
    while (currentClass != null && currentClass != Object.class) {
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

      // we also need to look for interface methods -
      // because the class may be abstract
      //获取接口的方法
      Class<?>[] interfaces = currentClass.getInterfaces();
      //递归获取
      for (Class<?> anInterface : interfaces) {
        //添加get方法排返回值类型  + 方法名 + 参数相同的方法
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }
      //递归获取
      currentClass = currentClass.getSuperclass();
    }
  1. resolveGetterConflicts解析最符合的get方法
  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    //循环遍历获取最符合的get方法,假设一个key->对应了两个value ,说明只有两种情况是符合的,第一种同一个类中同一个字段同时出现了get前缀和is前缀
    //第二种在父类中继承过来的,因为在上一步排除了返回值类型+方法名称+参数相同的方法,所以只有可能是父类的方法返回值>子类的
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      //符号条件的get方法
      Method winner = null;
      String propName = entry.getKey();
      //第一次赋值
      for (Method candidate : entry.getValue()) {
        if (winner == null) {
          winner = candidate;
          continue;
        }
        //获取返回值类型
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        //返回值类型相等的情况,就必须是boolean类型,因为 根据javeBean的定义名称一样的get方法,返回值也一样,那前缀肯定不一样
        //那么就只有一种可能 get 和is正对同一个字段同时存在用了is开头就必须是boolean类型否则不符合规范
        if (candidateType.equals(winnerType)) {
          if (!boolean.class.equals(candidateType)) {
            throw new ReflectionException(
                "Illegal overloaded getter method with ambiguous type for property "
                    + propName + " in class " + winner.getDeclaringClass()
                    + ". This breaks the JavaBeans specification and can cause unpredictable results.");
          } else if (candidate.getName().startsWith("is")) {
            //get和is同时存在 将is方法默认为get方法因为是boolean类型
            winner = candidate;
          }
          //判断candidateType 是否是 winnerType 的父类是的会默认就用winnerType 里面的get方法
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // OK getter type is descendant
          //如果相反则将candidate设为符合的get方法
        } else if (winnerType.isAssignableFrom(candidateType)) {
          winner = candidate;
        } else {
          throw new ReflectionException(
              "Illegal overloaded getter method with ambiguous type for property "
                  + propName + " in class " + winner.getDeclaringClass()
                  + ". This breaks the JavaBeans specification and can cause unpredictable results.");
        }
      }
      addGetMethod(propName, winner);
    }
  }
  1. 给getMethods赋值最终完成
  /**
   * 添加method方法
   * @param name 名称
   * @param method 方法
   */
  private void addGetMethod(String name, Method method) {
    //判断名称是否合法
    if (isValidPropertyName(name)) {
      //添加getMethods
      getMethods.put(name, new MethodInvoker(method));
      //获取返回值类型
      Type returnType = TypeParameterResolver.resolveReturnType(method, type);
      getTypes.put(name, typeToClass(returnType));
    }
  }

四、addSetMethods如何添加set方法

  1. 主方法addSetMethods 获取并解析set方法
  /**
   * 添加set方法
   * @param cls
   */
  private void addSetMethods(Class<?> cls) {
    Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
    //获取所有的方法
    Method[] methods = getClassMethods(cls);
    //获取set方法并设置conflictingSetters key-》方法名称  value 方法
    for (Method method : methods) {
      String name = method.getName();
      if (name.startsWith("set") && name.length() > 3) {
        if (method.getParameterTypes().length == 1) {
          //获取方法名称
          name = PropertyNamer.methodToProperty(name);
          //conflictingSetters添加
          addMethodConflict(conflictingSetters, name, method);
        }
      }
    }
    //解析
    resolveSetterConflicts(conflictingSetters);
  }
  1. resolveSetterConflicts方法解析set方法
  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    for (String propName : conflictingSetters.keySet()) {
      //获取这个名称下的set方法
      List<Method> setters = conflictingSetters.get(propName);
      //获取type的返回值类型,就是set的参数类型
      Class<?> getterType = getTypes.get(propName);
      Method match = null;
      ReflectionException exception = null;
      for (Method setter : setters) {
        //获取方法的参数类型
        Class<?> paramType = setter.getParameterTypes()[0];
        //相同
        if (paramType.equals(getterType)) {
          // should be the best match
          match = setter;
          break;
        }
        if (exception == null) {
          try {
            match = pickBetterSetter(match, setter, propName);
          } catch (ReflectionException e) {
            // there could still be the 'best match'
            match = null;
            exception = e;
          }
        }
      }
      if (match == null) {
        throw exception;
      } else {
        //添加set方法
        addSetMethod(propName, match);
      }
    }
  }
  1. 添加符合添加的set方法
private void addSetMethod(String name, Method method) {
    if (isValidPropertyName(name)) {
      setMethods.put(name, new MethodInvoker(method));
      Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
      setTypes.put(name, typeToClass(paramTypes[0]));
    }
  }

五、总结

其实添加get方法和添加set方法都是比较类似的一个操作,到现在mybatis的核心Reflector类就已经讲解完了。这个类的主要作用其实就是解析我们类的信息保存起来以便后续的使用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

久★伴i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值