千家信息网

mybatis源码如何分析反射模块

发表于:2025-01-30 作者:千家信息网编辑
千家信息网最后更新 2025年01月30日,mybatis源码如何分析反射模块,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。mybatis的反射模块,对应refle
千家信息网最后更新 2025年01月30日mybatis源码如何分析反射模块

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 setMethods = new HashMap<>();  /**   * 属性对应的getting方法的映射   * key:属性名称   * value:Invoker对象   */  private final Map getMethods = new HashMap<>();  /**   * 属性对应的setting方法的方法参数类型的映射   * key:属性名称   * value:方法参数类型   */  private final Map> setTypes = new HashMap<>();  /**   * 属性对应的getting方法的方法参数类型的映射   * key:属性名称   * value:方法参数类型   */  private final Map> getTypes = new HashMap<>();  /**   * 默认构造方法   */  private Constructor defaultConstructor;  /**   * 不区分大小写的属性集合   */  private Map 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> 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);  }
  • <1>处:Map> conflictingGetters:变量,属性与其getting方法的映射。因为父类和子类都可能定义了相同属性的getting方法,所以value会是个数组。

  • <2>处:addMethodConflict方法用到了Map的computeIfAbsent方法,这个方法仅jdk1.8即以上支持。这个方法很值得推荐。很惭愧,虽然1.8用了很久了,但是jdk提供的很多很便捷的方法,仍然没有使用,此处算是学到了。

 private void addMethodConflict(Map> conflictingMethods, String name, Method method) {    List list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());    list.add(method);  }

接下来,重点看resolveGetterConflicts方法

private void resolveGetterConflicts(Map> conflictingGetters) {    // 遍历每个属性,查找其最匹配的方法。因为子类可以覆写父类的方法,所以一个属性,可能对应多个 getting 方法    for (Entry> 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> 处,基于返回类型比较。重点在 <1.1> 和 <1.2> 的情况,因为子类可以修改放大返回值,所以在出现这个情况时,选择子类的该方法。例如,父类的一个方法的返回值为 List ,子类对该方法的返回值可以覆写为 ArrayList 。

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

0