MyBatis反射工具模块之Reflector

news/2024/5/19 5:20:38 标签: java, 反射, mybatis, Reflector

文章目录

  • 前言
  • 一、01. MyBatis反射工具模块之Reflector
    • 1. 参数注释说明
    • 2. 主要注释方法说明
    • 3. 主要注释子方法详细说明
      • 3.1 addDefaultConstructor
      • 3.1 addGetMethods, addSetMethods
      • 3.2 addFields
  • 总结


前言

用了两年多的Spring Data JPA, 再来熟读一下mybatis源码


Reflector_11">一、01. MyBatis反射工具模块之Reflector

1. 参数注释说明

java">  // 类型
  private final Class<?> type;
  // getter属性名称集合
  private final String[] readablePropertyNames;
  // setter属性名称集合
  private final String[] writablePropertyNames;
  // setter方法集合,key:属性,value: Invoker
  private final Map<String, Invoker> setMethods = new HashMap<>();
  // getter方法集合,key:属性,value: Invoker
  private final Map<String, Invoker> getMethods = new HashMap<>();
  // setter属性类型集合,key:属性名称,value:参数类型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  // getter属性类型集合,key:属性名称,value:返回值类型
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  // 无参构造
  private Constructor<?> defaultConstructor;
  // 属性名称集合, key: 大写属性名,value:属性名称
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

2. 主要注释方法说明

Reflector 主要方法就是构造函数 Reflector(Class<?> clazz),下面是该方法的注释说明

java">  //这个方法主要就是通过映射的方式给参数赋值
  public Reflector(Class<?> clazz) {
    // 类型
    type = clazz;
	// 默认无参构造函数赋值
    addDefaultConstructor(clazz);
    // getMethods,getTypes 赋值, getter方法属性名称以及返回值类型获取保存
    addGetMethods(clazz);
    // setMethods,setTypes 赋值, setter方法属性名称以及请求参数类型获取保存
    addSetMethods(clazz);
    // 该对象无getter,setter方法的属性(addGetMethods, addSetMethods 没扫描出来的属性),通过addFields给补上.例如无get,set方法的属性
    // setMethods,setTypes,getMethods,getTypes 赋值
    addFields(clazz);
    // getter属性名称集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    // setter属性名称集合
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    // 将属性名称保存到caseInsensitivePropertyMap
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

3. 主要注释子方法详细说明

3.1 addDefaultConstructor

java">  // 默认无参构造函数赋值
  private void addDefaultConstructor(Class<?> clazz) {
    // 获取该对象所有构造函数
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    // 筛选出无参构造函数并且赋值
    Arrays.stream(constructors)
      .filter(constructor -> constructor.getParameterTypes().length == 0)// 筛选出无参构造函数
      .findAny()// 获取无参构造函数
      .ifPresent(constructor -> this.defaultConstructor = constructor);// 给Reflector对象属性defaultConstructor赋值
  }

3.1 addGetMethods, addSetMethods

java">
  // getMethods,getTypes 赋值, getter方法属性名称以及返回值类型获取保存
  private void addGetMethods(Class<?> clazz) {
    // 用来装属性集合
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    // 获取此类所有的方法
    Method[] methods = getClassMethods(clazz);
    // 从getter方法中获取属性名称,加入conflictingGetters集合
    Arrays.stream(methods)
      .filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName())) // 筛选出符合getter的方法
      .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m)); // 加入conflictingGetters集合
    //解决getter 方法冲突
    resolveGetterConflicts(conflictingGetters);
  }
  
  // 获取此类所有的方法
  private Method[] getClassMethods(Class<?> clazz) {
    // key: 唯一方法key(返回类型#方法名称:参数列表), value:Method
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    while (currentClass != null && currentClass != Object.class) {
      // getDeclaredMethods 获取此类所有方法
      // addUniqueMethods给uniqueMethods赋值
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

      // 获取此类所有接口
      Class<?>[] interfaces = currentClass.getInterfaces();
      for (Class<?> anInterface : interfaces) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }
      // 获取此类的父类
      currentClass = currentClass.getSuperclass();
    }

    Collection<Method> methods = uniqueMethods.values();
    // 返回获取的所有方法
    return methods.toArray(new Method[0]);
  }
  
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    // 处理冲突的核心逻辑其实就是比较 getter 方法的返回值,优先选择返回值为子类的 getter 方法
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      // 属性method
      Method winner = null;
      // 属性名称
      String propName = entry.getKey();
      // 是否冲突
      boolean isAmbiguous = false;
      for (Method candidate : entry.getValue()) {
        if (winner == null) {
          winner = candidate;
          continue;
        }
        // 两个 Method 使用返回类型做比较
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        if (candidateType.equals(winnerType)) { // 返回类型有冲突
          if (!boolean.class.equals(candidateType)) { // 判断返回类型是否是boolean
            // 不是boolean 则判断为冲突
            isAmbiguous = true;
            break;
          } else if (candidate.getName().startsWith("is")) { // 为什么是boolean 就不判断为冲突呢?
            // 是boolean类型
            winner = candidate;
          }
        } else if (candidateType.isAssignableFrom(winnerType)) {// winnerType 是 candidateType 子类 或 winnerType实现了candidateType接口
          // OK getter type is descendant
        } else if (winnerType.isAssignableFrom(candidateType)) {// candidateType 是 winnerType 子类 或 candidateType实现了winnerType接口
          winner = candidate;
        } else {
          isAmbiguous = true;
          break;
        }
      }
      // 将属性名称加入集合
      addGetMethod(propName, winner, isAmbiguous);
    }
  }
  
private void addGetMethod(String name, Method method, boolean isAmbiguous) {
    MethodInvoker invoker = isAmbiguous
        ? new AmbiguousMethodInvoker(method, MessageFormat.format(
            "Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
            name, method.getDeclaringClass().getName()))
        : new MethodInvoker(method);
    getMethods.put(name, invoker);
    Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    getTypes.put(name, typeToClass(returnType));
  }

3.2 addFields

java">  private void addFields(Class<?> clazz) {
    // 获取该类所有字段(不包括父类字段)
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      // 添加 setMethods 集合中不存在的字段
      if (!setMethods.containsKey(field.getName())) {
        // 获取属性修饰符
        int modifiers = field.getModifiers();
        // 判断该属性修饰符是否是 final 或 static
        if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
          // 加入setMethods,setTypes集合
          addSetField(field);
        }
      }
      // 添加 getMethods 集合中不存在的字段
      if (!getMethods.containsKey(field.getName())) {
        addGetField(field);
      }
    }
    // 通过递归获取改类以及父类所有属性
    if (clazz.getSuperclass() != null) {
      addFields(clazz.getSuperclass());
    }
  }

欢迎补充~


总结

映射大法好!!!


http://www.niftyadmin.cn/n/1364806.html

相关文章

运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy),三大件(bigthree problem)...

一般的我们喜欢这样对对象赋值&#xff1a; Person p1;Person p2p1; classT object(another_object), or A a(b); classT object another object; class A { // … }; int main( ) { A x; A y(x); // … A z x; z y; } 这样的话&#xff0c;如果成员变量中有指针的话&am…

【1】STM32 Debug in RAM 在RAM中调试STM32 !!!

1.确定自己芯片的RAM起始地址和大小&#xff0c;例如我用的神舟王STM32F1底板 CPU为STMF103ZET 包含 64K RAM&#xff1a;0x2000 0000----0x2000 FFFF。 2.重新划分RAM 我将64K RAM分成两部分 48K&#xff08;0xC000&#xff09;用来当做Flash&#xff0c;16K&#xff08;0x4…

5697. 检查二进制字符串字段

5697. 检查二进制字符串字段 文章目录5697. 检查二进制字符串字段前言题目描述&#xff1a;一、我的答案前言 提示&#xff1a;leetcode做题一定要看清楚题目&#xff0c;如果不懂题目可以去评论区看看大家对题目的理解。 忌讳&#xff1a;不懂题目的意思就答题 题目描述&…

Mono for Android for Visual Studio 2010安装及试用

安装 Mono for Android for Visual Studio 2010 需要下面4个步骤&#xff1a; 1、安装 JDK 下载并安装 Java 1.6 (Java 6) JDK。 2、安装 Android SDK 下载地址&#xff1a;http://developer.android.com/sdk/index.html说明: Android SDK两种下载版本&#xff0c;一种是包含…

构成特定和需要添加的最少元素 - 贪心算法

5698. 构成特定和需要添加的最少元素 文章目录5698. 构成特定和需要添加的最少元素前言题目描述&#xff1a;一、解题关键一、我的答案1.1 注意内容:1.2 思路前言 提示&#xff1a;leetcode做题一定要看清楚题目&#xff0c;如果不懂题目可以去评论区看看大家对题目的理解。 忌…

ubuntu下安装firefox浏览器的flash插件

1&#xff0c;http://get.adobe.com/cn/flashplayer/ 下载tar.gz类型文件 2&#xff0c;解压tar.gz&#xff0c;将解压的usr的文件夹下的内容复制到/usr文件下&#xff08;在所在目录运行&#xff09; $ tar zxvf install_flash_player_11_linux.x86_64.tar.gz$ sudo cp libfla…

螺旋矩阵类通用思路

螺旋矩阵类通用思路 文章目录螺旋矩阵类通用思路前言解题思路54. 螺旋矩阵代码实现59. 螺旋矩阵 II代码实现前言 提示&#xff1a;leetcode做题一定要看清楚题目&#xff0c;如果不懂题目可以去评论区看看大家对题目的理解。 忌讳&#xff1a;不懂题目的意思就答题 解题思路 总…

Table View Programming Guide for iOS---(一)---About Table Views in iOS Apps

About Table Views in iOS Apps Table views are versatile user interface objects frequently found in iOS apps. A table view presents data in a scrollable list of multiple rows that may be divided into sections. 表格视图是通用用户界面对象&#xff0c;常常能在i…