美文网首页MyBatis源码剖析
[MyBatis源码分析 - 反射器模块 - 组件一] Refl

[MyBatis源码分析 - 反射器模块 - 组件一] Refl

作者: 小胡_鸭 | 来源:发表于2020-10-14 00:47 被阅读0次

    一、简介

      Reflector是整个反射器模块的基础,每个Reflector对象都对应一个类,在其构造函数中,根据传入的Class对象,调用Java的反射API,获取并缓存反射操作需要用到的这个类的元信息。

    二、成员属性

      private Class<?> type;      // 对应的Class类型
      private String[] readablePropertyNames = EMPTY_STRING_ARRAY;  // 可读属性的名称集合,可读属性就是存在相应getter方法的属性,初始值为空数组
      private String[] writeablePropertyNames = EMPTY_STRING_ARRAY; // 可写属性的名称集合,可写属性就是存在相应setter方法的属性,初始值为空数组
      private Map<String, Invoker> setMethods = new HashMap<String, Invoker>();   // 记录了属性相应的setter方法,key是属性名称,value是Invoker对象,其封装了setter方法对应的Method对象
      private Map<String, Invoker> getMethods = new HashMap<String, Invoker>();   // 属性相应的getter方法集合,key是属性名称,value是Invoker对象
      private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();    // 记录了属性相应的setter方法的参数值类型,key是属性名称,value是setter方法的参数类型
      private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();    // 记录了属性相应的getter方法的返回值类型,key是属性名称,value是getter方法的返回值类型
      private Constructor<?> defaultConstructor;                    // 记录了默认的构造方法
    
      private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>(); // 记录了所有属性名称的集合,key统一记录成大写
    
    • type:传入的需要解析元信息的类,每个 Reflector 对象都对应一个类。
    • readablePropertyNames:可读属性的名称集合,一般Java Bean中属性定义为私有,要读取属性值,必须通过 getter 方法来获取,换句话说,假如属性有相应的 getter 方法,我们就认定为该属性为可读属性。假如属性名为 username ,则方法名一般为 getUsername() ,假如属性是布尔值如 open ,则方法名一般为 isOpen()
    • writeablePropertyNames:可写属性的名称集合,同样的类似上面的,如果属性有相应的 setter 方法,可认定为可写属性。
    • setMethods:实际运行中如将SQL执行返回结果集映射为Java对象,设置对象属性的操作需要反射来完成,这些操作封装在 Invoker 对象中,该对象缓存属性对应的 Field 对象,设置属性值依赖该 Field 对象完成;key对应属性名称,value对应 Invoker 对象。
    • getMethods:同样获取对象属性值需要反射来完成,原理跟上面类似,不过反射的操作由 set 变成 get
    • setTypes:不管是执行SQL前参数的预编译,还是执行完之后将结果集映射为Java对象,都需要知道属性的类型,底层的JDBC对象才知道要调用哪些相应的方法;本属性缓存了属性名和属性的 setter 方法的映射关系。
    • getTypes:同上,本属性缓存了属性名和属性的 getter 方法的映射关系。
    • defaultConstructor:传入解析的类的默认构造方法,常用于通过反射创建Java对象。
    • caseInsensitivePropertyMap:记录了所有属性名称的集合,key统一处理成大写,value则为属性名。

    三、构造方法

      Reflector的构造方法中,会解析执行的Class对象,并填充上述集合,源码如下:

      public Reflector(Class<?> clazz) {
        type = clazz;                   // 初始化type字段
        addDefaultConstructor(clazz);   // 查找clazz的默认构造方法(无参构造方法),通过反射获取所有构造方法,通过遍历找出参数长度为0的构造方法
        addGetMethods(clazz);           // 获取clazz中的getter方法,并填充getMethods集合和getTypes集合
        addSetMethods(clazz);           // 获取clazz中的setter方法,并填充setMethods集合和setTypes集合
        addFields(clazz);               // 处理没有getter/setter的字段
    
        // 根据getMethods/setMethods集合,初始化可读/写属性的名称集合
        readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    
        // 初始化caseInsensitivePropertyMap集合,其中记录了所有大写格式的属性名称
        for (String propName : readablePropertyNames) {
          caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
        for (String propName : writeablePropertyNames) {
          caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
      }
    

      首先初始化 type 属性,然后在 addDefaultConstructor() 方法中根据传入的Class对象的默认构造方法初始化 defaultConstructor 属性,接着调用 addGetMethods() 方法填充 getMethodsgetTypes 集合,同理调用 addSetMethods() 方法填充 setMethodssetTypes 集合,调用 addFields() 方法去处理没有getter/setter的其余字段,因为底层本质是通过反射来获取和设置属性值,该方法也会填充上述的四个集合,最后再将收集到的属性名根据允许获取和设置值来填充 readablePropertyNameswriteablePropertyNames 集合,再将这两个集合中的所有属性名的大写作为key来填充 caseInsensitivePropertyMap

    四、方法功能

    1、addDefaultConstructor(Class<?> clazz)

    【功能】获取类的默认构造方法。
    【源码与注释】

      private void addDefaultConstructor(Class<?> clazz) {
        // 获取类的所有构造方法
        Constructor<?>[] consts = clazz.getDeclaredConstructors();
        // 遍历所有构造方法
        for (Constructor<?> constructor : consts) {
          // 找到无参构造方法,该方法即为默认构造方法
          if (constructor.getParameterTypes().length == 0) {
            // 构造方法有可能被private修饰,需要设置其可以访问
            if (canAccessPrivateMethods()) {
              try {
                constructor.setAccessible(true);
              } catch (Exception e) {
                // Ignored. This is only a final precaution, nothing we can do.
              }
            }
            // 如果无参构造方法有访问的权限,则赋值给defaultConstructor
            if (constructor.isAccessible()) {
              this.defaultConstructor = constructor;
            }
          }
        }
      }
    

    2、canAccessPrivateMethods()

    【功能】判断系统安全管理器是否允许修改类的可访问性。
    【源码与注释】

      // 判断是否可以修改可访问性
      private static boolean canAccessPrivateMethods() {
        try {
          // 获取系统安全管理器
          SecurityManager securityManager = System.getSecurityManager();
          // 如果设置了安全管理器,检查是否有通过反射来访问protected、private的成员和方法的权限
          if (null != securityManager) {
            securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
          }
        } catch (SecurityException e) {
          // 如果没有权限,则权限检查会抛出异常,返回false表示不允许访问私有方法
          return false;
        }
        // 如果未设置安全管理器,或者有指定权限,则返回true表示允许访问私有方法
        return true;
      }
    

    3、addGetMethods()

    【功能】获取类的 getter 方法并填充 getMethodsgetTypes 集合。
    【源码与注释】

      private void addGetMethods(Class<?> cls) {
        // (1)定义属性名和对应getter方法的映射
        Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
        // (2)获取当前类及其父类中定义的所有方法
        Method[] methods = getClassMethods(cls);
        // (3)遍历所有方法,筛选出 getter 方法并添加到 conflictingGetters 中
        for (Method method : methods) {
          String name = method.getName();
          // 筛选出getter方法: 一般属性名为aaBb对应的getter方法为getAaBb()
          //                  如果属性是布尔值,则对应的getter方法为isAaBb()
          // 如果子类重写覆盖了父类getter方法导致方法签名不一致
          // eg:
          //   父类定义:  List<String> getList()      方法签名为: java.util.List<String>#getList
          //   子类定义: ArrayList<String> getList() 方法签名为: java.util.ArrayList<String>#getList
          // 我们要取的肯定是返回参数类型更加具体的方法,但是方法签名不一致导致冲突,就先将属性名和对应的冲突方法缓存到conflictingGetters
          // 再交给resolveGetterConflicts()方法进一步处理
          // (3.1)以 get 和 is 开头,并且方法名长度分别长于 3 和 2 才表明这是个 getter 方法
          if (name.startsWith("get") && name.length() > 3) {
            // (3.2)只有方法参数个数为0的方法才是 getter 方法
            if (method.getParameterTypes().length == 0) {
              // (3.3)根据方法名获取属性名
              name = PropertyNamer.methodToProperty(name);
              // (3.4)属性名和方法的映射添加到 conflictingGetters 中
              addMethodConflict(conflictingGetters, name, method);
            }
          } else if (name.startsWith("is") && name.length() > 2) {
            if (method.getParameterTypes().length == 0) {
              name = PropertyNamer.methodToProperty(name);
              addMethodConflict(conflictingGetters, name, method);
            }
          }
        }
        // (4)解决签名不同但是方法重复的 getter 方法,一般属性的 getter 只需要有一个即可
        resolveGetterConflicts(conflictingGetters);
      }
    

    【解析】
    (1)定义属性名和 getter 方法的映射,因为子类可能重写父类方法,比如返回值类型更加具体,所以映射的值是一个列表。
    (2)调用 #getClassMethods() 方法来获取类的所有方法,包括实现接口定义的方法、继承自父类的方法,并沿着继承链向上追溯,具体实现可阅读下面关于该方法的详解。
    (3)遍历所有方法,筛选出 getter 方法

    • (3.1)只有形如 getXxx()isYyy() 的方法才是 getter 方法
    • (3.2)getter 方法的参数个数应该为0
    • (3.3)根据方法名获得属性名
    • (3.4)属性名和方法的映射添加到 conflictingGetters 中,如果属性名第一次添加方法,则创建List,否则添加到已经创建的List中,源码如下:
      private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
        // 获取List
        List<Method> list = conflictingMethods.get(name);
        // List为空,说明该属性是第一次,先创建List,再将List添加到conflictingMethods中
        if (list == null) {
          list = new ArrayList<Method>();
          conflictingMethods.put(name, list);
        }
        // 往Method List中添加Method
        list.add(method);
      }
    

    (4)调用 #resolveGetterConflicts(Map<String, List<Method>>) 方法解决签名不同但是方法重复的 getter 方法的问题,一般属性的 getter 只需要有一个即可,详解参见下面对该方法的介绍。

    4、getClassMethods(Class<?> cls)

    【功能】获取类cls及其超类中声明的方法,及其实现的接口方法,使用本方法而不是更简单的 Class.getMethods() 是因为我们也想获取被声明为 private 的方法。
    【源码与注释】

      private Method[] getClassMethods(Class<?> cls) {
        // (1)用于记录指定类中定义的全部方法的唯一签名以及对应的Method对象
        Map<String, Method> uniqueMethods = new HashMap<String, Method>();
        // (2)表示当前在while循环体中被解析声明方法所属的类,将从cls到cls的父类,最终为Object类
        Class<?> currentClass = cls;
        while (currentClass != null) {
          // (3)记录currentClass这个类中定义的全部方法
          // PS.只能获取类内定义的普通方法,对于继承的方法无法通过反射获取
          addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
    
          // (4)我们也要寻找接口方法,因为类可能是抽象的,可能实现了接口方法,这些方法无法通过上面的反射获取
          // we also need to look for interface methods -
          // because the class may be abstract
          Class<?>[] interfaces = currentClass.getInterfaces();
          for (Class<?> anInterface : interfaces) {
            addUniqueMethods(uniqueMethods, anInterface.getMethods());
          }
    
          // (5)获取父类,继续循环,获取从父类继承的方法
          currentClass = currentClass.getSuperclass();
        }
    
        // (6)将 uniqueMethods 中的值即方法取出来放 methods 集合中
        Collection<Method> methods = uniqueMethods.values();
    
        // (7)将 methods 集合转化为 Method 数组返回
        return methods.toArray(new Method[methods.size()]);
      }
    

    【解析】
    (1)定义局部变量 uniqueMethods:存储方法签名和方法的映射关系,方法签名可以看成是每个方法的唯一标识,其组成规则可以看下面关于 #addUniqueMethods() 方法的详解。
    (2)定义局部变量 currentClass:表示当前被解析方法的类,将一直沿着继承链向上循环,从cls到cls的父类,最终为Object类。
    (3)调用 #addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) 方法记录当前类中定义的所有方法到 uniqueMethods 中,其源码实现如下:

      private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
        for (Method currentMethod : methods) {
          // (3.1)过滤掉桥接方法,桥接方法不是类中定义的方法,而是编译器为了兼容自动生成的方法
          if (!currentMethod.isBridge()) {
            // (3.2)获取方法签名
            // 通过Reflector.getSignature()方法得到的方法签名是: 返回值类型#方法名称:参数类型列表。
            // 例如,Reflector.getSignature(Method)方法的唯一签名是:
            // java.lang.String#getSignature:java.lang.reflect.Method
            // 通过Reflector.getSignature()方法得到的方法签名是全局唯一的,可以作为该方法的唯一标识
            String signature = getSignature(currentMethod);
    
            // (3.3)检测在子类中是否已经添加过该方法,如果在子类中已经添加过,则表示子类覆盖了该方法,
            // 无须再想uniqueMethods集合中添加该方法了
            // check to see if the method is already known
            // if it is known, then an extended class must have
            // overridden a method
            if (!uniqueMethods.containsKey(signature)) {
              if (canAccessPrivateMethods()) {
                try {
                  currentMethod.setAccessible(true);
                } catch (Exception e) {
                  // Ignored. This is only a final precaution, nothing we can do.
                }
              }
              // (3.4)记录该签名和方法的对应关系
              uniqueMethods.put(signature, currentMethod);
            }
          }
        }
      }
    
    • (3.1)首先过滤掉桥接方法,所谓桥接方法,就是编译器为了兼容JDK自动生成而不是代码中定义的方法
    • (3.2)获取方法签名,源码如下:
      private String getSignature(Method method) {
        StringBuilder sb = new StringBuilder();
        // 返回类型
        Class<?> returnType = method.getReturnType();
        if (returnType != null) {
          sb.append(returnType.getName()).append('#');
        }
        // 方法名
        sb.append(method.getName());
        // 方法参数类型
        Class<?>[] parameters = method.getParameterTypes();
        for (int i = 0; i < parameters.length; i++) {
          if (i == 0) {
            sb.append(':');
          } else {
            sb.append(',');
          }
          sb.append(parameters[i].getName());
        }
        return sb.toString();
      }
    

    其组成规则为:方法返回值类型#方法名:参数1类型,参数2类型,参数3类型,...,参数名n类型
    eg:void#addUniqueMethods:Map<String, Method>,Method[]

    • (3.3)因为子类可能重写父类的方法,因此当前类为上一次循环类的父类时,有可能父类被子类重写的方法的签名跟子类是一样的,这种情况以子类中声明的方法为准,如果方法是私有的,则添加访问权限。
    • (3.4)将方法签名和方法的映射添加到 uniqueMethods 中。

    (4)获取当前类实现的接口,并遍历接口调用 #addUniqueMethods(Map<String, Method>, Method[]) 获得接口中定义的方法。
    (5)获取父类,继续循环,获取从父类继承的方法。
    (6)循环结束后,将 uniqueMethods 中的值即获取到的类的所有方法放到 methods 集合中。
    (7)将 methods 集合转化为 Method 数组返回。

    5、resolveGetterConflicts(Map<String, List<Method>> conflictingGetters)

    【功能】解决一个属性有多个 getter 方法的问题,并调用 #addGetMethod(String name, Method method) 方法真正填充 getMethodsgetTypes 集合。
    【源码与注释】

      private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
        // (1)对每个属性的方法逐个处理
        for (String propName : conflictingGetters.keySet()) {
          List<Method> getters = conflictingGetters.get(propName);
          Iterator<Method> iterator = getters.iterator();
          Method firstMethod = iterator.next();
          // (2)该字段只有一个getter方法,没有冲突,直接添加到getMethods集合并填充getTypes集合
          if (getters.size() == 1) {
            addGetMethod(propName, firstMethod);
          } else {
            // (3)同一属性名称存在多个getter方法,则需要对比这些getter方法的返回值,选择getter方法
            // 迭代过程中的临时变量,用于记录迭代到目前为止,最适合作为getter方法的Method
            Method getter = firstMethod;
            // (3.1)记录返回值类型
            Class<?> getterType = firstMethod.getReturnType();
            while (iterator.hasNext()) {
              Method method = iterator.next();
              Class<?> methodType = method.getReturnType();
              if (methodType.equals(getterType)) {
                // (3.2)返回值相同,实际上没有冲突,证明方法签名生成那个可能有问题了,抛出异常
                throw new ReflectionException("Illegal overloaded getter method with ambiguous type for property "
                    + propName + " in class " + firstMethod.getDeclaringClass()
                    + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
              } else if (methodType.isAssignableFrom(getterType)) {
                // (3.3)假如判断结果为true,表示methodType是getterType的继承父类或实现的接口
                // 当前最合适的方法(getter)的返回值是当前方法(method)返回值的子类,什么都不做,当前最适合的方法依然不变
                // OK getter type is descendant
              } else if (getterType.isAssignableFrom(methodType)) {
                // (3.4)当前方法的返回值是当前最适合的方法的返回值的子类,更新临时变量getter,当前的getter方法成为最适合的getter方法
                getter = method;
                getterType = methodType;
              } else {
                // (3.5)返回值相同,二义性,抛出异常
                throw new ReflectionException("Illegal overloaded getter method with ambiguous type for property "
                    + propName + " in class " + firstMethod.getDeclaringClass()
                    + ".  This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
              }
            }
            // (4)while循环结束筛选出最合适的方法之后,调用addGetMethod()方法填充getMethods和getTypes集合
            addGetMethod(propName, getter);
          }
        }
      }
    

    【解析】
    (1)遍历逐个处理每个属性,因为子类可以覆盖重写父类的方法,所以一个属性可能有多个 getter 方法
    (2)如果属性只有一个 getter 方法,没有冲突,则直接调用 #addGetMethod(String name, Method method) 方法填充处理。
    (3)同一属性可能有多个 getter 方法,定义局部变量 getter 存储当前最合适的getter

    • (3.1)定义局部变量 getterType,记录 getter 的返回类型。
    • (3.2)因为 getter 方法名相同,且不带参数,所以如果子类和父类方法返回值类型完全相同,说明要么子类画蛇添足方法跟父类相同如下所示,要么证明生成方法签名的方法可能有问题,此时抛出异常。
    // 父类
    public class SupClass {
        private List userList;
        public List getUserList() {
            return userList;
        }
    }
    // 子类
    public class SubClass {
        private List userList;
        public List getUserList() {
            return userList;
        }
    }
    
    • (3.3)如果遍历到的其他 getter 方法的返回值类型是当前最适合的 getter 方法返回值类型的父类,则 getter 依然是最合适的方法,举例如下:
    // 父类
    public class SupClass() {
        private List userList;
        // 当前遍历到的method,methodType为List
        public List getUserList() {
            return userList;
        }
    }
    // 子类
    public class SubClass {
        private ArrayList userList;
        // 当前遍历筛选到的最合适的getter,getterType为ArrayList
        public ArrayList getUserList() {
            return userList;
        }
    }
    
    • (3.4)跟上面的情况反过来,只是此时 getter 为父类的 getUserList 方法,getterTypeList,这时遍历到更合适的子类的方法,因为其返回类型更加具体,所以更新替换 gettergetterType
    • (3.5)如果不同的 getter 只是刚好方法名相同,实际上返回类型既不完全相同,也没有继承派生关系,则证明开发人员写的类不符合 JavaBean 的规范,此时直接抛出异常。
    • (4)找到最合适的 getter 方法之后,调用 #addGetMethod(String name, Method method) 方法填充处理。

    6、addGetMethod(String name, Method method)

    【功能】筛选合法的属性名,将方法封装成 MethodInvoker 作为 value,属性名作为 key 填充 getMethods 集合;解析方法返回的真正类型(因为有可能带泛型、通配符等)作为 value,属性名作为 key 填充 getTypes 集合。
    【源码与注释】

      private void addGetMethod(String name, Method method) {
        // (1)筛选过滤掉非法的属性名
        if (isValidPropertyName(name)) {
          // (2)将属性名以及对应的MethodInvoker对象添加到getMethods集合中,MethodInvoker是对Method对象反射操作的封装
          getMethods.put(name, new MethodInvoker(method));
    
          // (3)因为方法返回值中可能带有泛型,eg: List<T>
          // 所以通过TypeParameterResolver.resolveReturnType()方法将类型信息解析到Type对象中
          Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    
          // (4)将属性名称和对应的返回值类型添加到getTypes集合中保存
          getTypes.put(name, typeToClass(returnType));
        }
      }
    

    【解析】
    (1)调用 #isValidPropertyName(String) 方法过滤出合理的属性名,合理的标准是:

    • 属性名不以 $ 开头 + 属性名不为 serialVersionUID (类实现序列化接口要定义的属性)+ 属性名不为 class
      其源码如下:
      private boolean isValidPropertyName(String name) {
        return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
      }
    

    (2)根据 method 对象封装创建 MethodInvoker 作为 value,属性名为 key 填充 getMethods 集合。
    (3)调用 #TypeParameterResolver.resolveReturnType(Method method, Type srcType) 解析返回值的具体类型(关于该工具类的解析过程可参考:[MyBatis源码分析 - 反射器模块 - 组件四] TypeParameterResolver
    ),并调用 #typeToClass(Type src) 方法将返回类型对应的 Type 对象转化为 Class 对象并填充 getTypes 集合。,其源码如下:

      private Class<?> typeToClass(Type src) {
        Class<?> result = null;
        // 普通类型,直接使用类
        if (src instanceof Class) {
          result = (Class<?>) src;
        // 参数化类型,使用原始类型,如参数化类型List<String>的原始类型是List
        } else if (src instanceof ParameterizedType) {
          result = (Class<?>) ((ParameterizedType) src).getRawType();
        // 泛型数据类型,形如A<T>[]或T[],前者的数组元素类型是ParameterizedType,后者是TypeVariable
        // 当然,经过TypeParameterResolver的处理,里面的泛型已被解析为具体类型,如List<String>[]、String[]
        } else if (src instanceof GenericArrayType) {
          Type componentType = ((GenericArrayType) src).getGenericComponentType();
          // 数组元素已经是一个具体类型,直接返回形如String[]对应的Class对象即可
          if (componentType instanceof Class) {
            result = Array.newInstance((Class<?>) componentType, 0).getClass();
          } else {
            // 数组元素依然不是一个普通类型,如List<String>[],递归调用typetoClass方法处理,如上会返回List
            // 最终返回的result为List[]
            Class<?> componentClass = typeToClass(componentType);
            result = Array.newInstance((Class<?>) componentClass, 0).getClass();
          }
        }
        if (result == null) {
          result = Object.class;
        }
        return result;
      }
    

    7、addSetMethods(Class<?> cls)

    【功能】获取类的 setter 方法,并提取属性名,填充 setMethodssetTypes 集合。
    【源码与注解】

      private void addSetMethods(Class<?> cls) {
        // 属性名与其 setter 方法的映射
        Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
        // 获取类的所有方法
        Method[] methods = getClassMethods(cls);
        // 遍历所有方法
        for (Method method : methods) {
          String name = method.getName();
          // 筛选出符合 setter 方法名规则的方法
          if (name.startsWith("set") && name.length() > 3) {
            // setter 方法的参数个数应该为1
            if (method.getParameterTypes().length == 1) {
              // 根据方法名提取出属性名,eg: getUser() => user
              name = PropertyNamer.methodToProperty(name);
              // 将属性与其 setter 方法的映射添加到conflictingSetters中
              addMethodConflict(conflictingSetters, name, method);
            }
          }
        }
        // 解决同个属性名有多个冲突的 setter 方法的问题
        resolveSetterConflicts(conflictingSetters);
      }
    

    【解析】
      跟 #addGetMethods(Class<?> cls) 方法的处理过程大同小异,setter 方法不管参数类型是否布尔值,方法名都形如 setXxx,并且参数个数必须为1,然后提取属性名,添加到 conflictingSetters 映射中,最后调用 #resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) 方法处理 setter 方法冲突。

    8、resolveSetterConflicts(Map<String, List<Method>> conflictingSetters)

    【功能】为类的每个属性挑选出最合适的 setter 方法,并调用 #addSetMethod(String name, Method method) 方法处理填充 setMethodssetTypes 集合。
    【源码与注解】

      private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
        // (1)逐个处理每个属性,为其找到最合适的 setter 方法,因为子类可以覆盖父类的方法,所以一个属性可能有多个 setter 方法
        for (String propName : conflictingSetters.keySet()) {
          List<Method> setters = conflictingSetters.get(propName);
          Method firstMethod = setters.get(0);
          // (2)只有一个 setter 方法,直接调用addSetMethod方法填充setMethods和setTypes集合
          if (setters.size() == 1) {
            addSetMethod(propName, firstMethod);
          } else {
            // (3)获取正在筛选 setter 方法的属性的 getter 方法的返回类型,应该跟 setter 的参数类型一致
            Class<?> expectedType = getTypes.get(propName);
            // (3.1)属性有setter却没有getter方法,存在二义性,不符合JavaBeans的规范,直接抛出异常
            if (expectedType == null) {
              throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
                  + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
                  "specification and can cause unpredicatble results.");
            } else {
              Iterator<Method> methods = setters.iterator();
              Method setter = null;
              while (methods.hasNext()) {
                Method method = methods.next();
                // (3.2)setter参数个数应该只有一个并且参数类型跟getter方法的返回值类型相同,不符合则循环
                if (method.getParameterTypes().length == 1
                    && expectedType.equals(method.getParameterTypes()[0])) {
                  setter = method;
                  break;
                }
              }
              // (4)如果循环结束还没找到setter方法,则抛出异常
              if (setter == null) {
                throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
                    + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
                    "specification and can cause unpredicatble results.");
              }
              // (5)while循环结束筛选出最合适的方法之后,调用addSetMethod()方法填充setMethods和setTypes集合
              addSetMethod(propName, setter);
            }
          }
        }
      }
    

    【详解】
    (1)(2)步的处理与 #resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) 类似。
    (3)一个符合 JavaBean 规范的类应该同时有 getter 和 setter,并且 getter 的返回类型和 setter 的参数类型一致;程序先去处理 getter,所以这里先根据属性名从 getTypes 获取到 getter 的返回类型。

    • (3.1)如果返回类型为空,证明该属性没有 getter 方法,这里抛出异常。
    • (3.2)如果不为空,则遍历所有的 setter,找到参数个数为1并且参数类型与 getter 返回类型相同的方法,找到即赋值给变量 setter ,并跳出循环。
      (4)如果没找到符合规范的 setter 方法,则抛出异常。
      (5)调用 #addSetMethod(String name, Method method) 方法真正填充 setMethodssetTypes 集合,其代码很简单,同样跟上面的 #addGetMethod(String name, Method method) 类似,源码如下:
      private void addSetMethod(String name, Method method) {
        // 筛选过滤掉非法的属性名
        if (isValidPropertyName(name)) {
          // 将属性名以及对应的MethodInvoker对象添加到setMethods集合中,MethodInvoker是对Method对象反射操作的封装
          setMethods.put(name, new MethodInvoker(method));
    
          // 因为方法返回值中可能带有泛型,eg: List<T>
          // 所以通过TypeParameterResolver.resolveReturnType()方法将类型信息解析到Type对象中
          Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
    
          // 将属性名称和对应的返回值类型添加到setTypes集合中保存
          setTypes.put(name, typeToClass(paramTypes[0]));
        }
      }
    

    9、addFields(Class<?> clazz)

    【功能】对于没有 getter 和 setter 的属性,通过本方法处理,填充到 getMethodsgetTypessetMethodssetTypes集合中,在获取设置这些属性值时,是通过 Field 对象的反射方法 getset实现的,是对 #addSetMethods(...)#addGetMethods(...) 方法的补充。
    【源码与注解】

      private void addFields(Class<?> clazz) {
        // (1)获取类中声明的所有field属性,包含了已经解析出来的有 getter&setter 的属性
        Field[] fields = clazz.getDeclaredFields();
        // 遍历field属性
        for (Field field : fields) {
          // 设置私有属性访问权限
          if (canAccessPrivateMethods()) {
            try {
              field.setAccessible(true);
            } catch (Exception e) {
              // Ignored. This is only a final precaution, nothing we can do.
            }
          }
          if (field.isAccessible()) {
            // (2)过滤掉已经有 getter 和 setter 的属性
            if (!setMethods.containsKey(field.getName())) {
              // (2.1)获取类的修饰符,过滤掉被声明为static final的属性,这些属性只能被类加载器设置其初始值
              int modifiers = field.getModifiers();
              if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
                // (2.2)
                addSetField(field);
              }
            }
            if (!getMethods.containsKey(field.getName())) {
              // (2.3)
              addGetField(field);
            }
          }
        }
        // (3)获取父类,找出父类中声明的属性
        if (clazz.getSuperclass() != null) {
          addFields(clazz.getSuperclass());
        }
      }
    

    【详解】
    (1)获取类中声明的所有field属性,包含了已经解析出来的有 getter&setter 的属性。
    (2)过滤掉已经有 getter&setter 的属性。

    • (2.1)过滤掉被声明为 static final 的属性,这类属性只允许被类加载器初始化。
    • (2.2)调用 #addSetField(Field field) 填充 setMethodssetTypes 集合,源码如下:
      private void addSetField(Field field) {
        // 过滤掉非法的属性
        if (isValidPropertyName(field.getName())) {
          // 创建封装了属性对应Field对象的SetFieldInvoker对象,内部利用Field的set方法来反射设置属性值
          setMethods.put(field.getName(), new SetFieldInvoker(field));
          // 解析获取属性值的具体类型(如果带泛型或通配符)
          Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
          // 将属性和属性值类型对应的Class对象映射填充setTypes集合
          setTypes.put(field.getName(), typeToClass(fieldType));
        }
      }
    
    • (2.3)调用 #addGetField(Field field) 填充 getMethodsgetTypes 集合,源码如下:
      private void addGetField(Field field) {
        // 过滤掉非法的属性
        if (isValidPropertyName(field.getName())) {
          // 创建封装了属性对应Field对象的GetFieldInvoker对象,内部利用Field的get方法来反射设置属性值
          getMethods.put(field.getName(), new GetFieldInvoker(field));
          // 解析获取属性值的具体类型(如果带泛型或通配符)
          Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
          // 将属性和属性值类型对应的Class对象映射填充setTypes集合
          getTypes.put(field.getName(), typeToClass(fieldType));
        }
      }
    

    (3)获取父类,通过递归调用本方法找出父类中声明的属性。

    10、其他方法

    【功能】主要是对 Reflector 类中的属性的使用,对外暴露使用的方法。

    • (1)#hasDefaultConstructor():判断被解析的类是否有构造方法。
    • (2)#getSetInvoker(String propertyName):根据属性名获取对应的 Invoker 对象,可通过该对象设置属性值。
    • (3)#getGetInvoker(String propertyName):根据属性名获取对应的 Invoker 对象,可通过该对象获取属性值。
    • (4)#getSetterType(String propertyName):根据属性名获取对应的 setter 方法的参数类型对应的 Class 对象。
    • (5)#getGetterType(String propertyName):根据属性名获取对应的 getter 方法的返回值类型对应的 Class 对象。
    • (6)#getGetablePropertyNames():获取类的所有可读属性。
    • (7)#getSetablePropertyNames():获取类的所有可写属性。
    • (8)#hasSetter(String propertyName):判断传入的属性名是否有 setter,可用来判断被解析的类是否有某属性或是否可设置某属性。
    • (9)#hasGetter(String propertyName):判断传入的属性名是否有 getter,可用来判断被解析的类是否有某属性或是否可读取某属性。
    • (10)#findPropertyName(String name):根据传入不区分大小写的属性名获取实际真实的属性名。

    五、ReflectorFactory

      ReflectorFactory 是一个接口,定义了创建和缓存 Reflector 对象的方法,只有一个实现类 DefaultReflectorFactory,类关系图如下:


      ReflectorFactory 接口定义如下:
    public interface ReflectorFactory {
      // 判断和设置是否有缓存Reflector对象
      boolean isClassCacheEnabled();
    
      void setClassCacheEnabled(boolean classCacheEnabled);
    
      // 根据传入的Class对象,创建对应的Reflector对象
      Reflector findForClass(Class<?> type);
    }
    

    【解析】
      #isClassCacheEnabled()#setClassCacheEnabled() 方法提供了调用代码Reflector工厂对象是否有缓存生成的 Reflector 对象的信息,#findForClass(Class<?> type) 方法则是根据传入的 Class 对象创建 Reflector 对象。

      唯一一个实现类 DefaultReflectorFactory 的源码如下:

    public class DefaultReflectorFactory implements ReflectorFactory {
      // 判断是否要缓存Reflector对象的标志位
      private boolean classCacheEnabled = true;
      // Reflector对象缓存
      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) {
        // 如果有缓存,则先尝试从缓存中取,如果缓存中没有则先创建Reflector对象,再放入缓存,最后返回
        if (classCacheEnabled) {
                // synchronized (type) removed see issue #461
          Reflector cached = reflectorMap.get(type);
          if (cached == null) {
            cached = new Reflector(type);
            reflectorMap.put(type, cached);
          }
          return cached;
        } else {
          // 如果没有缓存,直接创建Reflector对象即可
          return new Reflector(type);
        }
      }
    }
    

    【解析】
      代码比较简单,通过一个布尔型的标志位 classCacheEnabled 判断是否要对生成的 Reflector 对象进行缓存,前两个接口方法的实现也依赖于这个标志位;定义了一个并发容器 reflectorMap 缓存 Class 对象及 Reflector 对象的映射关系,将并发控制委托给 ConcurrentMap,#findForClass() 方法中,如果有缓存,先从缓存中取,如果不为空直接返回,如果为空则先创建再缓存最后返回,如果没缓存,则每次都直接创建返回。

    六、测试案例

      先来个简单的,不带继承关系,不带泛型的JavaBean,如下:

    public class ReflectorTest {
        // 简单Java类
        static class SimpleJavaBean {
            private Long id;
            private String username;
            private double money;
    
            public Long getId() {
                return id;
            }
    
            public void setId(Long id) {
                this.id = id;
            }
    
            public String getUsername() {
                return username;
            }
    
            public void setUsername(String username) {
                this.username = username;
            }
    
            @Override
            public String toString() {
                return "{ id:" + id + ", username:" + username + ", money:" + money + " }";
            }
        }
    }
    

      测试案例覆盖了 Reflector 对外暴露的所有方法,单元测试代码如下:

        @Test
        public void testSimpleJavaBean() throws InvocationTargetException, IllegalAccessException {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(SimpleJavaBean.class);
            Assert.assertTrue(reflector.hasDefaultConstructor());
    
            String[] readablePropertyNames = reflector.getGetablePropertyNames();
            Assert.assertEquals(3, readablePropertyNames.length);
            // 输出 money id username,虽然money没有显式的getter,但是可以通过反射获取,所以也是可读属性
            System.out.println("[readablePropertyNames] ");
            for (String name : readablePropertyNames)
                System.out.print(name + " ");
    
            System.out.println();
            String[] writeablePropertyNames = reflector.getSetablePropertyNames();
            Assert.assertEquals(3, writeablePropertyNames.length);
            System.out.println("[writeablePropertyNames]");
            for (String name : writeablePropertyNames)
                System.out.print(name + " ");
            System.out.println();
    
            Assert.assertEquals(Long.class, reflector.getGetterType("id"));
            Assert.assertEquals(String.class, reflector.getSetterType("username"));
            Assert.assertEquals(double.class, reflector.getGetterType("money"));
    
            Assert.assertTrue(reflector.hasGetter("id"));
            Assert.assertTrue(reflector.hasSetter("money"));
    
            Assert.assertEquals("id", reflector.findPropertyName("ID"));
            Assert.assertEquals("username", reflector.findPropertyName("Username"));
    
            SimpleJavaBean bean = new SimpleJavaBean();
            System.out.println("bean(before call Invoker): " + bean);
            Invoker idSetInvoker = reflector.getSetInvoker("id");
            idSetInvoker.invoke(bean, new Object[]{100L});
            Invoker idGetInvoker = reflector.getGetInvoker("id");
            Assert.assertEquals(100L, idGetInvoker.invoke(bean, new Object[] {}));
            Invoker usernameSetInvoker = reflector.getSetInvoker("username");
            usernameSetInvoker.invoke(bean, new Object[]{"zhangsan"});
            Invoker moneySetInvoker = reflector.getSetInvoker("money");
            moneySetInvoker.invoke(bean, new Object[]{2.33});
            System.out.println("bean(after call Invoker): " + bean);
        }
    

      执行结果如下:



      复杂一点,经过上面的源码分析可知,解析类属性时不仅会解析类中定义的,还会通过解析接口和父类获取 getter/setter 方法,最后解析出属性名和属性类型,所以定义一个测试接口和实现了接口的抽象父类,最后被测试的类继承了抽象父类,代码如下:

        // 接口定义
        static interface Entity<T> {
            T getId();
            void setId(T id);
        }
    
        // 抽象父类实现了接口
        static abstract class AbstractEntity implements Entity<Long> {
            private Long id;
    
            @Override
            public Long getId() {
                return id;
            }
    
            @Override
            public void setId(Long id) {
                this.id = id;
            }
        }
    
        // 类继承了父类,间接实现了接口
        static class Section extends AbstractEntity {
        }
    

      测试代码聚焦是否能获取到继承的属性和正确解析类型,如下:

        @Test
        public void testGetSetterType() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Section.class);
            Assert.assertEquals(Long.class, reflector.getSetterType("id"));
        }
    
        @Test
        public void testGetGetterType() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Section.class);
            Assert.assertEquals(Long.class, reflector.getGetterType("id"));
        }
    
        @Test
        public void shouldNotGetClass() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Section.class);
            Assert.assertFalse(reflector.hasGetter("class"));
        }
    

      执行结果:



      更复杂一些,属性的类型可能是比较复杂的类型,比如链表List、数组,属性可能是私有的,一般带泛型的类是设计用来复用的,其中子类继承带泛型的父类并且指定类型是一种常用的形式,所以类定义如下:

      static abstract class Parent<T extends Serializable> {
        protected T id;
        protected List<T> list;
        protected T[] array;
        private T fld;
        public T pubFld;
        public T getId() {
          return id;
        }
        public void setId(T id) {
          this.id = id;
        }
        public List<T> getList() {
          return list;
        }
        public void setList(List<T> list) {
          this.list = list;
        }
        public T[] getArray() {
          return array;
        }
        public void setArray(T[] array) {
          this.array = array;
        }
        public T getFld() {
          return fld;
        }
      }
    
      static class Child extends Parent<String> {
      }
    

      测试代码如下:

        // 测试是否能解析出泛型
        @Test
        public void shouldResolveSetterParam() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            assertEquals(String.class, reflector.getSetterType("id"));
        }
    
        // 测试解析复杂的类型链表
        @Test
        public void shouldResolveParameterizedSetterParam() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            assertEquals(List.class, reflector.getSetterType("list"));
        }
    
        // 测试解析复杂的类型数组
        @Test
        public void shouldResolveArraySetterParam() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            Class<?> clazz = reflector.getSetterType("array");
            assertTrue(clazz.isArray());
            assertEquals(String.class, clazz.getComponentType());
        }
    
        @Test
        public void shouldResolveGetterType() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            assertEquals(String.class, reflector.getGetterType("id"));
        }
    
        @Test
        public void shouldResolveSetterTypeFromPrivateField() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            assertEquals(String.class, reflector.getSetterType("fld"));
        }
    
        @Test
        public void shouldResolveGetterTypeFromPublicField() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            assertEquals(String.class, reflector.getGetterType("pubFld"));
        }
    
        @Test
        public void shouldResolveParameterizedGetterType() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            assertEquals(List.class, reflector.getGetterType("list"));
        }
    
        @Test
        public void shouldResolveArrayGetterType() throws Exception {
            ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
            Reflector reflector = reflectorFactory.findForClass(Child.class);
            Class<?> clazz = reflector.getGetterType("array");
            assertTrue(clazz.isArray());
            assertEquals(String.class, clazz.getComponentType());
        }
    

      执行结果:


      通过上面几个测试案例,可以比较好地学习掌握 Reflector 的用法。

    相关文章

      网友评论

        本文标题:[MyBatis源码分析 - 反射器模块 - 组件一] Refl

        本文链接:https://www.haomeiwen.com/subject/rtxepktx.html