mybatis源码如何分析反射模块

发布时间:2021-10-19 17:38:56 作者:柒染
来源:亿速云 阅读:122

mybatis源码如何分析反射模块,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

mybatis的反射模块,对应reflection包。反射模块作为myabits中的基础支持层,对java原生的反射进行了良好的封装,提供了简洁易用的api,方便上层调用,并对反射操作做了一系列优化,例如缓存了类的元数据,提高了反射操作的性能。

今天重点看下Reflector

org.apache.ibatis.reflection.Reflector,反射器,每个Reflector对应一个类。Reflector会缓存反射操作需要的类的信息,例如:构造方法、属性名、setting/getting方法等等。

public class Reflector {

  /**
   * 对应的类
   */
  private final Class<?> type;
  /**
   * 可读属性数组
   */
  private final String[] readablePropertyNames;
  /**
   * 可写属性数组
   */
  private final String[] writablePropertyNames;
  /**
   * 属性对应的setting方法的映射
   * key:属性名称
   * value:Invoker对象
   */
  private final Map<String, Invoker> setMethods = new HashMap<>();
  /**
   * 属性对应的getting方法的映射
   * key:属性名称
   * value:Invoker对象
   */
  private final Map<String, Invoker> getMethods = new HashMap<>();
  /**
   * 属性对应的setting方法的方法参数类型的映射
   * key:属性名称
   * value:方法参数类型
   */
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  /**
   * 属性对应的getting方法的方法参数类型的映射
   * key:属性名称
   * value:方法参数类型
   */
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  /**
   * 默认构造方法
   */
  private Constructor<?> defaultConstructor;

  /**
   * 不区分大小写的属性集合
   */
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
  
  public Reflector(Class<?> clazz) {
        // 设置对应的类
        type = clazz;
        // <1> 初始化 defaultConstructor
        addDefaultConstructor(clazz);
        // <2> // 初始化 getMethods 和 getTypes ,通过遍历 getting 方法
        addGetMethods(clazz);
        // <3> // 初始化 setMethods 和 setTypes ,通过遍历 setting 方法。
        addSetMethods(clazz);
        // <4> // 初始化 getMethods + getTypes 和 setMethods + setTypes ,通过遍历 fields 属性。
        addFields(clazz);
        // <5> 初始化 readablePropertyNames、writeablePropertyNames、caseInsensitivePropertyMap 属性
        readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        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);
        }
    }

addGetMethods


addGetMethods(Class<?> cls),代码如下:

private void addGetMethods(Class<?> cls) {
    // <1> 属性与其 getting 方法的映射
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    //获取所有方法
    Method[] methods = getClassMethods(cls);
    for (Method method : methods) {
      //参数大于0,说明不是getting方法,忽略
      if (method.getParameterTypes().length > 0) {
        continue;
      }
      //以get或者is开头,说明是getting方法
      String name = method.getName();
      if ((name.startsWith("get") && name.length() > 3)
          || (name.startsWith("is") && name.length() > 2)) {
        //获取属性
        name = PropertyNamer.methodToProperty(name);
        //<2> 添加到conflictingGetters
        addMethodConflict(conflictingGetters, name, method);
      }
    }
    //解决getting的冲突方法(一个属性,只保留一个对应的方法)
    resolveGetterConflicts(conflictingGetters);
  }
 private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
    list.add(method);
  }

接下来,重点看resolveGetterConflicts方法

private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    // 遍历每个属性,查找其最匹配的方法。因为子类可以覆写父类的方法,所以一个属性,可能对应多个 getting 方法
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
        Method winner = null; // 最匹配的方法
        String propName = entry.getKey();
        for (Method candidate : entry.getValue()) {
            // winner 为空,说明 candidate 为最匹配的方法
            if (winner == null) {
                winner = candidate;
                continue;
            }
            // <1> 基于返回类型比较
            Class<?> winnerType = winner.getReturnType();
            Class<?> candidateType = candidate.getReturnType();
            // 类型相同
            if (candidateType.equals(winnerType)) {
                // 返回值类型相同,应该在 getClassMethods 方法中,已经合并。所以抛出 ReflectionException 异常
                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.");
                // 选择 boolean 类型的 is 方法
                } else if (candidate.getName().startsWith("is")) {
                    winner = candidate;
                }
            // 不符合选择子类
            } else if (candidateType.isAssignableFrom(winnerType)) {
                // OK getter type is descendant
            // <1.1> 符合选择子类。因为子类可以修改放大返回值。例如,父类的一个方法的返回值为 List ,子类对该方法的返回值可以覆写为 ArrayList 。
            } else if (winnerType.isAssignableFrom(candidateType)) {
                winner = candidate;
            // <1.2> 返回类型冲突,抛出 ReflectionException 异常
            } 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.");
            }
        }
        // <2> 添加到 getMethods 和 getTypes 中
        addGetMethod(propName, winner);
    }
}

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

推荐阅读:
  1. MyBatis源码浅析
  2. 通过源码分析MyBatis的缓存

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mybatis

上一篇:mybatis相关介绍是什么

下一篇:xss跨站脚本攻击是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》