反射工具-Reflector

作者: 93张先生 | 来源:发表于2020-09-19 15:40 被阅读0次

My Batis 在进行参数处理、结果映射等操作时,会涉及大量的反射操作。 Java 中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码, MyBatis 提供了专门的反射模块,该模块位于 org.apache.ibatis.reflection包中,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射 API 。

UML

image.png

ReflectorFactory

创建 Reflector 对象,工厂模式接口

public interface ReflectorFactory {

  boolean isClassCacheEnabled();

  void setClassCacheEnabled(boolean classCacheEnabled);

  Reflector findForClass(Class<?> type);
}

DefaultReflectorFactory

反射工厂,用于生产 Reflector 对象,并缓存Reflector 对象

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

  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) {
      // synchronized (type) removed see issue #461
//      If the specified key is not already associated with a value,
//      attempts to compute its value using the given mapping function
//      and enters it into this map unless {@code null}.
//      absent 缺席的;如果type值不存在,就应用后面的函数,计算出来,计算结果非空,放入map中;并返回计算值;
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      return new Reflector(type);
    }
  }

}

Reflector

Reflector 是 MyBatis 中反射模块的基础,每个 Reflector 对象都对应一个类,在 Reflector 中缓存了反射操作需要使用的类的元信息,包含原始类、类的构造方法、 写字段、读字段、getter 方法、setter 方法 、方法的返回类型。

public class Reflector {

  private final Class<?> type; //类名称 Section
  private final String[] readablePropertyNames; //可读的类属性 id
  private final String[] writablePropertyNames; //可写的类属性 id
  // Invoker 是一个封装了一个类的 GetField、SetFiled、getter 和 setter 方法 的对象,供后面反射调用类的字段、getter、setter 方法,方便使用
  private final Map<String, Invoker> setMethods = new HashMap<>(); //<属性,set方法> id,setId
  private final Map<String, Invoker> getMethods = new HashMap<>();
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  private final Map<String, Class<?>> getTypes = new HashMap<>(); //<属性,get方法> id,java.lang.Long
  private Constructor<?> defaultConstructor;

  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();  //元素为属性大小写不敏感的; ID :id

  public Reflector(Class<?> clazz) {
    type = clazz;
    addDefaultConstructor(clazz); //赋值默认的构造函数
    addGetMethods(clazz);
    addSetMethods(clazz);
    addFields(clazz); //处理没有getter、setter方法的字段
    // Set.toArray(T[] a), a 这参数有两个作用,1.指定数组的类型,2.指定数组的大小;如果数组的大小为0,只采用这个数组的类型,大小根据 Set 集合元素的个数;a 数组的大小不为0:新数组大小为 a 这个数组指定的参数大小
    /**
     * 对于Set而言,它只知道它内部保存的是Object,所以默认情况下,toArray只能是返回一个由这些Object构成的Object数组出来。但程序的作者或许更清楚其内部元素的更具体的类型,因此,HashSet类提供了toArray的另一个重载版本,允许用户指定一种比Object[]更具体的数组类型,方法是传递一个用户想要的数组类型的一个数组实例进去,多长都无所谓(因此我们常常使用一个0长度的,毕竟把类型带进去就OK了),于是,toArray内部就会按照你想要的这种类型,给构造一个数组出来。这样构造出来的数组,当然是很安全地被调用者转换回那个实际的类型。
     */
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    //读写属性重复,会覆盖
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

  /**
   * 添加默认的构造方法
   * @param clazz
   */
  private void addDefaultConstructor(Class<?> clazz) {
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    // filter 过滤出满足表达式的元素
    Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
      .findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
  }

  private void addGetMethods(Class<?> clazz) {//class org.apache.ibatis.reflection.ReflectorTest$Section
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    //4 methods
//    public abstract java.lang.Object org.apache.ibatis.reflection.ReflectorTest$Entity.getId()
//    public java.lang.Long org.apache.ibatis.reflection.ReflectorTest$AbstractEntity.getId()
//    public void org.apache.ibatis.reflection.ReflectorTest$AbstractEntity.setId(java.lang.Long)
//    public abstract void org.apache.ibatis.reflection.ReflectorTest$Entity.setId(java.lang.Object)
    // 获取类的 getter 方法们
    Method[] methods = getClassMethods(clazz);
    // addMethodConflict() 是将一个类的一个属性,的多个 getter 方法,放入 conflictingGetters 集合中,key 是 属性名称,value 是 这个属性的所有的 getter 方法,包含父类继承的等 getter 方法。
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    //解决多个类、多个接口 的继承 和实现 之间的 多个getter方法 冲突;保留最小辈(下限)类的get方法
    resolveGetterConflicts(conflictingGetters);
  }
  //解决多个类、多个接口 的继承 和实现 之间的 多个getter方法 冲突;保留最小辈(下限)类的get方法,
  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      Method winner = null;
      String propName = entry.getKey();
      boolean isAmbiguous = false; //模糊不清的;这个属性的方式 是否是 模糊不清楚的;如果一个类是模糊不清楚的;最终会抛出AmbiguousMethodInvoker
      for (Method candidate : entry.getValue()) {
        if (winner == null) {
          winner = candidate;
          continue;
        }
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        if (candidateType.equals(winnerType)) {
          if (!boolean.class.equals(candidateType)) {
            isAmbiguous = true;
            break;
          } else if (candidate.getName().startsWith("is")) {
            winner = candidate;
          }
//          Object.isAssignableFrom(String.class)  true --> string is assignable from object ; string 是被检查的类; object 是 string supper 类
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // OK getter type is descendant
        } else if (winnerType.isAssignableFrom(candidateType)) {
          winner = candidate;
        } else {
          isAmbiguous = true;
          break;
        }
      }
      // 类的属性,类的正确的 getter 方法,getter 方法 封装成 Invoker 对象,放入 getMethods 集合中,然后返回类型放入 getTypes 集合中
      addGetMethod(propName, winner, isAmbiguous);
    }
  }

  /**
   * 将这个类的这个属性,这个属性的 getter 方法,封装成 MethodInvoker 对象,方便后面反射时,对属性的 getter 方法的操作,
   * 如果 isAmbiguous 是 true,则会创建一个 AmbiguousMethodInvoker,来提示这个 getter 方法不确实,是不清楚的,方便后面报错
   * @param name 类的属性名称
   * @param method 类的属性,对应的 getter 方法
   * @param isAmbiguous 这个属性的 getter 是否是确定的,是唯一的,不是模糊的
   */
  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);
    // 查找 getter 方法的 返回数据类型
    Type returnType = TypeParameterResolver.resolveReturnType(method, type);
    // 放入 getTypes 集合中
    getTypes.put(name, typeToClass(returnType));
  }

  private void addSetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingSetters = new HashMap<>();
    Method[] methods = getClassMethods(clazz);
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveSetterConflicts(conflictingSetters);
  }

  /**
   * 处理一个类的属性的多个getter 或者 setter 方法;
   * @param conflictingMethods key 是一个类的属性,value 是这个属性的所有的 getter 或者 setter 方法
   * @param name
   * @param method
   */
  private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    // 是否有效的属性值
    if (isValidPropertyName(name)) {
      List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
      list.add(method);
    }
  }

  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    for (String propName : conflictingSetters.keySet()) {
      List<Method> setters = conflictingSetters.get(propName);
      Class<?> getterType = getTypes.get(propName);
      boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;
      boolean isSetterAmbiguous = false;
      Method match = null;
      for (Method setter : setters) {
        if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {
          // should be the best match
          match = setter;
          break;
        }
        if (!isSetterAmbiguous) {
          match = pickBetterSetter(match, setter, propName);
          isSetterAmbiguous = match == null;
        }
      }
      if (match != null) {
        addSetMethod(propName, match);
      }
    }
  }

  /**
   * 冲突的 setter 方法,选择一个合适的 setter 方法返回
   * @param setter1
   * @param setter2
   * @param property
   * @return
   */
  private Method pickBetterSetter(Method setter1, Method setter2, String property) {
    if (setter1 == null) {
      return setter2;
    }
    Class<?> paramType1 = setter1.getParameterTypes()[0];
    Class<?> paramType2 = setter2.getParameterTypes()[0];
    if (paramType1.isAssignableFrom(paramType2)) {
      return setter2;
    } else if (paramType2.isAssignableFrom(paramType1)) {
      return setter1;
    }
    MethodInvoker invoker = new AmbiguousMethodInvoker(setter1,
        MessageFormat.format(
            "Ambiguous setters defined for property ''{0}'' in class ''{1}'' with types ''{2}'' and ''{3}''.",
            property, setter2.getDeclaringClass().getName(), paramType1.getName(), paramType2.getName()));
    setMethods.put(property, invoker);
    Type[] paramTypes = TypeParameterResolver.resolveParamTypes(setter1, type);
    setTypes.put(property, typeToClass(paramTypes[0]));
    return null;
  }

  private void addSetMethod(String name, Method method) {
    MethodInvoker invoker = new MethodInvoker(method);
    setMethods.put(name, invoker);
    Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
    setTypes.put(name, typeToClass(paramTypes[0]));
  }

  //将一个类型,转化为相应的class类
  private Class<?> typeToClass(Type src) {
    Class<?> result = null;
    if (src instanceof Class) {
      result = (Class<?>) src;
    } else if (src instanceof ParameterizedType) {
      result = (Class<?>) ((ParameterizedType) src).getRawType();
    } else if (src instanceof GenericArrayType) {
      Type componentType = ((GenericArrayType) src).getGenericComponentType();
      if (componentType instanceof Class) {
        result = Array.newInstance((Class<?>) componentType, 0).getClass();
      } else {
        Class<?> componentClass = typeToClass(componentType);
        result = Array.newInstance(componentClass, 0).getClass();
      }
    }
    if (result == null) {
      result = Object.class;
    }
    return result;
  }
  //一个递归的调用关系;处理类的field,方便反射调用,将这个字段的对应方法分别 添加到 setMethods、getMethods、setTypes、getTypes 这几个集合中,方便使用
  private void addFields(Class<?> clazz) {
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      if (!setMethods.containsKey(field.getName())) {
        // issue #379 - removed the check for final because JDK 1.5 allows
        // modification of final fields through reflection (JSR-133). (JGB)
        // pr #16 - final static can only be set by the classloader
        int modifiers = field.getModifiers();
        if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
          // 处理类的 set 字段和字段的返回类型
          addSetField(field);
        }
      }
      if (!getMethods.containsKey(field.getName())) {
        // 处理类的 get 字段和字段的返回类型
        addGetField(field);
      }
    }
    if (clazz.getSuperclass() != null) {
      addFields(clazz.getSuperclass());
    }
  }

  /**
   * 处理类的 set 字段和字段的返回类型
   * @param field
   */
  private void addSetField(Field field) {
    if (isValidPropertyName(field.getName())) {
      setMethods.put(field.getName(), new SetFieldInvoker(field));
      Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
      setTypes.put(field.getName(), typeToClass(fieldType));
    }
  }

  /**
   * 处理类的 get 字段和字段的返回类型
   * @param field
   */
  private void addGetField(Field field) {
    if (isValidPropertyName(field.getName())) {
      getMethods.put(field.getName(), new GetFieldInvoker(field));
      Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
      getTypes.put(field.getName(), typeToClass(fieldType));
    }
  }
  //是否有效的属性值
  private boolean isValidPropertyName(String name) {
    return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
  }

  /**
   * 获取类的所有的 getter 方法
   * 获取类的所有的 getter 方法
   * This method returns an array containing all methods
   * declared in this class and any superclass.
   * We use this method, instead of the simpler <code>Class.getMethods()</code>,
   * because we want to look for private methods as well.
   *
   * @param clazz The class
   * @return An array containing all methods in this class
   */
  private Method[] getClassMethods(Class<?> clazz) {
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    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) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }

      currentClass = currentClass.getSuperclass(); // 获取父类
    }

    Collection<Method> methods = uniqueMethods.values();

    return methods.toArray(new Method[0]);
  }

  /**
   * 解决 getter 方法的唯一性
   * @param uniqueMethods
   * @param methods
   */
  private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      if (!currentMethod.isBridge()) {
        String signature = getSignature(currentMethod);
        // 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)) {
          uniqueMethods.put(signature, currentMethod);
        }
      }
    }
  }

  /**
   * getter 方法的签名,确定方法的唯一性
   * 签名:返回值类型#方法名称:参数类型列表
   * @param method
   * @return
   */
  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++) {
      sb.append(i == 0 ? ':' : ',').append(parameters[i].getName());
    }
    return sb.toString();
  }

  /**
   * 检测成员的访问权限
   * Checks whether can control member accessible.
   *
   * @return If can control member accessible, it return {@literal true}
   * @since 3.5.0
   */
  public static boolean canControlMemberAccessible() {
    try {
      SecurityManager securityManager = System.getSecurityManager();
      if (null != securityManager) {
        securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
      }
    } catch (SecurityException e) {
      return false;
    }
    return true;
  }

  /**
   * Gets the name of the class the instance provides information for.
   *
   * @return The class name
   */
  public Class<?> getType() {
    return type;
  }

  public Constructor<?> getDefaultConstructor() {
    if (defaultConstructor != null) {
      return defaultConstructor;
    } else {
      throw new ReflectionException("There is no default constructor for " + type);
    }
  }

  public boolean hasDefaultConstructor() {
    return defaultConstructor != null;
  }

  public Invoker getSetInvoker(String propertyName) {
    Invoker method = setMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }

  public Invoker getGetInvoker(String propertyName) {
    Invoker method = getMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }

  /**
   * Gets the type for a property setter.
   * 获取一个属性的,一个setter()方法的属性类型
   *
   * @param propertyName - the name of the property
   * @return The Class of the property setter
   */
  public Class<?> getSetterType(String propertyName) {
    Class<?> clazz = setTypes.get(propertyName);
    if (clazz == null) {
      throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    }
    return clazz;
  }

  /**
   * Gets the type for a property getter.
   *
   * @param propertyName - the name of the property
   * @return The Class of the property getter
   */
  public Class<?> getGetterType(String propertyName) {
    Class<?> clazz = getTypes.get(propertyName);
    if (clazz == null) {
      throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    }
    return clazz;
  }

  /**
   * Gets an array of the readable properties for an object.
   *
   * @return The array
   */
  public String[] getGetablePropertyNames() {
    return readablePropertyNames;
  }

  /**
   * Gets an array of the writable properties for an object.
   *
   * @return The array
   */
  public String[] getSetablePropertyNames() {
    return writablePropertyNames;
  }

  /**
   * Check to see if a class has a writable property by name.
   *
   * @param propertyName - the name of the property to check
   * @return True if the object has a writable property by the name
   */
  public boolean hasSetter(String propertyName) {
    return setMethods.keySet().contains(propertyName);
  }

  /**
   * Check to see if a class has a readable property by name.
   *
   * @param propertyName - the name of the property to check
   * @return True if the object has a readable property by the name
   */
  public boolean hasGetter(String propertyName) {
    return getMethods.keySet().contains(propertyName);
  }

  public String findPropertyName(String name) {
    return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
  }
}

Invoker

Invoker 调用者、是对象 getter 或者 setter 方法、get一个字段、set 一个字段,这几种类方法和属性的封装,方便放射对象调用。

UML

image.png
public interface Invoker {
  Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

  Class<?> getType();
}

GetFieldInvoker

类字段 get 方法的封装

public class GetFieldInvoker implements Invoker {
  // get 一个类的字段
  private final Field field;

  public GetFieldInvoker(Field field) {
    this.field = field;
  }

  /**
   * 调用 target 对象的 字段,并返回字段值
   * @param target
   * @param args
   * @return
   * @throws IllegalAccessException
   */
  @Override
  public Object invoke(Object target, Object[] args) throws IllegalAccessException {
    try {
      return field.get(target);
    } catch (IllegalAccessException e) {
      if (Reflector.canControlMemberAccessible()) {
        field.setAccessible(true);
        return field.get(target);
      } else {
        throw e;
      }
    }
  }

  /**
   * 返回字段的数据类型
   * @return
   */
  @Override
  public Class<?> getType() {
    return field.getType();
  }
}

SetFieldInvoker

类字段 set 方法的封装

public class SetFieldInvoker implements Invoker {
  private final Field field;

  public SetFieldInvoker(Field field) {
    this.field = field;
  }

  @Override
  public Object invoke(Object target, Object[] args) throws IllegalAccessException {
    try {
      field.set(target, args[0]);
    } catch (IllegalAccessException e) {
      if (Reflector.canControlMemberAccessible()) {
        field.setAccessible(true);
        field.set(target, args[0]);
      } else {
        throw e;
      }
    }
    return null;
  }

  @Override
  public Class<?> getType() {
    return field.getType();
  }
}

MethodInvoker

一个类的 getter 和 setter 方法的封装,方便反射调用,这个类的getter 和 setter 方法,进行 属性赋值。

public class MethodInvoker implements Invoker {

  // getter、setter 方法的类型
  private final Class<?> type; //java.lang.Long
  // getter、setter 方法
  private final Method method; //public java.lang.Long org.apache.ibatis.reflection.ReflectorTest$AbstractEntity.getId()

  public MethodInvoker(Method method) {
    this.method = method;
    //set()
    if (method.getParameterTypes().length == 1) {
      type = method.getParameterTypes()[0];
    } else {
      //get()
      type = method.getReturnType();
    }
  }

  /**
   * 调用 target 对象的 getter、setter 方法
   * @param target
   * @param args
   * @return
   * @throws IllegalAccessException
   * @throws InvocationTargetException
   */
  @Override
  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
    try {
      return method.invoke(target, args);
    } catch (IllegalAccessException e) {
      if (Reflector.canControlMemberAccessible()) {
        method.setAccessible(true);
        return method.invoke(target, args);
      } else {
        throw e;
      }
    }
  }

  /**
   * 返回 getter 方法的返回数据类型,setter 方法的参数类型
   * @return
   */
  @Override
  public Class<?> getType() {
    return type;
  }
}

AmbiguousMethodInvoker

一个类的属性的 getter、setter 方法时多个时,不确定哪一个是的 标志类,方便后续使用的时候报错。

public class AmbiguousMethodInvoker extends MethodInvoker {
  // getter、setter  方法不确实时的错误信息
  private final String exceptionMessage;

  public AmbiguousMethodInvoker(Method method, String exceptionMessage) {
    super(method);
    this.exceptionMessage = exceptionMessage;
  }

  @Override
  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
    throw new ReflectionException(exceptionMessage);
  }
}

相关文章

网友评论

    本文标题:反射工具-Reflector

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