mybatis之Reflector和ReflectorFactory

Reflector和ReflectorFactory

ReflectorFactory

/**
 * 工厂接口的默认实现
 */
public class DefaultReflectorFactory implements ReflectorFactory {
  //该字段决定了是否对开启对Reflector的缓存
  private boolean classCacheEnabled = true;
  //使用线程安全的map对Reflector对象进行缓存
  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;
  }

  /**
   * 生产Reflector对象
   * @param type 目标类型
   * @return 目标类型的Reflector对象
   */
  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) { // 允许缓存
      // 生产入参type的反射器对象,并放入缓存
      // java8之后。上面的操作可以简化为一行,若key对应的value为空,会将第二个参数的返回值存入并返回
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      return new Reflector(type);
    }
  }

}

很简单,主要是用一个线程安全的ConcurrentHashMap对Relectoru进行缓存。

Reflector

public class Reflector {

  // 要被反射解析的类
  private final Class<?> type;
  // 能够读的属性列表,即有get方法的属性列表
  private final String[] readablePropertyNames;
  // 能够写的属性列表,即有set方法的属性列表
  private final String[] writablePropertyNames;
  // set方法映射表。键为属性名,值为对应的set方法
  private final Map<String, Invoker> setMethods = new HashMap<>();
  // get方法映射表。键为属性名,值为对应的get方法
  private final Map<String, Invoker> getMethods = new HashMap<>();
  // set方法输入类型。键为属性名,值为对应的该属性的set方法的类型(实际为set方法的第一个参数的类型)
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  // get方法输出类型。键为属性名,值为对应的该属性的get方法的类型(实际为get方法的返回值类型)
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  // 默认构造函数
  private Constructor<?> defaultConstructor;
  // 大小写无关的属性映射表。键为属性名全大写值,值为属性名
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

这个类的主要目的是通过一个类的Class类,将这个类的各种方法,与根据javaBean约定获取get/set方法(获取属性名然后get/set拼接),然后将解析的出来的各个方法名称等(包括实现的接口,继承的父类)。这个类的初始化就是通过一个目标Class类进行分析,将其所有的内容分别存储到Reflector中。

/**
   * Reflector的构造方法
   * @param clazz 需要被反射处理的目标类
   */
  public Reflector(Class<?> clazz) {
    // 要被反射解析的类
    type = clazz;
    // 设置默认构造器属性
    addDefaultConstructor(clazz);
    // 处理clazz中的getter方法,填充getMethods集合和getTypes集合
    addGetMethods(clazz);
    // 解析所有有setter
    addSetMethods(clazz);
    // 解析所有属性
    addFields(clazz);
    // 设定可读属性
    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);
    }
  }
addDefaultConstructor(clazz)
   Constructor<?>[] constructors = clazz.getDeclaredConstructors();
   // 无参构造函数即为默认构造函数
   Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
     .findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
 }
  • constructor.getParameterTypes() – 获取构造方法的参数类型数组
addGetMethods(clazz) – 存储所有的get方法
/**
   * 找出类中的get方法
   * @param clazz 需要被反射处理的目标类
   */
  private void addGetMethods(Class<?> clazz) {
    // 存储属性的get方法。Map的键为属性名,值为get方法列表。某个属性的get方法用列表存储是因为前期可能会为某一个属性找到多个可能get方法。
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    
    // 找出该类中所有的方法
    Method[] methods = getClassMethods(clazz);
    // 过滤出get方法,过滤条件有:无入参、符合Java Bean的命名规则;然后取出方法对应的属性名、方法,放入conflictingGetters
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    // 如果一个属性有多个疑似get方法,resolveGetterConflicts用来找出合适的那个
    resolveGetterConflicts(conflictingGetters);
  }
  • Method[] methods = getClassMethods(clazz); – 获取所有的get方法,包括私有,父类和实现的接口get方法等。
private Method[] getClassMethods(Class<?> clazz) {
    //用于记录指定类中定义的全部方法的唯一签名以及对应的 Method 对象
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    while (currentClass != null && currentClass != Object.class) {
      //记录 currentClass 这个类中定义的全部方法
      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]);
  }
  • 这段代码分为了三部分:在类不为空和不是Object类的情况下
  • 从当前类获取所有的方法
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
  • 从实现的接口的获取所有的方法
    Class<?>[] interfaces = currentClass.getInterfaces();
      for (Class<?> anInterface : interfaces) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
    }
  • 从继承的父类中获取所有的方法
currentClass = currentClass.getSuperclass();

我们一个一个来看:

addUniqueMethods 从当前类获取所有的方法
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      if (!currentMethod.isBridge()) {
        //通过 Reflector.getSignature () 方法得到的方法签名是:返回值类型#方法名称:参
        //数类型列表 例如, Reflector.getSignature(Method )方法的唯一签名是:
        //java.lang.String#getSignature:]java.lang.reflect.Method
        //通过 Reflector.getSignature()方法得到的方法签名是全局唯一的,可以作为该方法的唯一标识
        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);
        }
      }
    }
  }
  • currentMethod.isBridge()桥接方法,是为了兼容用的。
  • String signature = getSignature(currentMethod); – mybatis自己设计的一个方法,其目的是为了给方法一个唯一签名,并记录到uniqueMethods中。签名格式:返回值类型#方法名称:参数类型列表
    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();
  }
从其实现的接口中获取方法
  • Class<?>[] interfaces = currentClass.getInterfaces();很简单,就不多说了
从父类中获取实现的方法
  • currentClass = currentClass.getSuperclass();获取到父类后,继续走while循环,也很简单。

至此,获取该类所有的方法已经结束,需要对其进行过滤已找出符合要求的get方法

 // 过滤出get方法,过滤条件有:无入参、符合Java Bean的命名规则;然后取出方法对应的属性名、方法,放入conflictingGetters
   Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
     .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));

过滤的原则:

  • m.getParameterTypes().length == 0 方法的参数类型集合的长度为0
  • PropertyNamer.isGetter(m.getName()) 判断方法的名称是否符合要求
  • addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m)
// 将方法名转化为属性名
 public static String methodToProperty(String name) {
   if (name.startsWith("is")) {
     name = name.substring(2);
   } else if (name.startsWith("get") || name.startsWith("set")) {
     name = name.substring(3);
   } else {
     throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
   }

   // 将方法名中属性的大小写修改正确
   if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
     name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
   }

   return name;
 }
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    // 如果没有则创建列表,有则取出列表
    // computeIfAbsent(name, k -> new ArrayList<>()) 方法为Map中的方法:
    // 如果map中通过name索引到value,则返回value
    // 否则将name作为输入交给lambda函数,直接lambda(name),则将name:lambda(name)写入map,并返回lambda(name)
    List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
    // 增加一个方法进入列表
    list.add(method);
  }

这段代码的是将参数名和方法集合(有可能有多个get方法)进行映射的真正代码

  • conflictingMethods.computeIfAbsent(key,k -> value);
  • 将key放入map中,如果key对应的之不存在则返回null,如果存在则返回value。
    此处将computeIfAbsent的value返回(List),将方法添加到List集合中去。

下面开始解决存在多个get方法的冲突问题:resolveSetterConflicts(conflictingSetters);

// 如果一个属性有多个getter方法,该方法负责找出真正的
  private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
      Method winner = null;
      String propName = entry.getKey();
      // 对key的多个getter进行循环
      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)) {
            // 不是布尔型,两个返回一样,则无法辨明到底哪个是getter
            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.");
          } else if (candidate.getName().startsWith("is")) {
            // 是is的当选
            winner = candidate;
          }
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // candidateType返回了子类,则winner作为最终结果
        } else if (winnerType.isAssignableFrom(candidateType)) {
          // winner返回了子类,则candidate作为最终结果
          winner = candidate;
        } 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.");
        }
      }
      // 最后放入的是属性:唯一的getter方法
      addGetMethod(propName, winner);
    }
  }
  • 先判断两个get方法的返回值类型是否相同,不相同在比较是否是布尔值
  • 在判断两个方法返回值类型是否为对方的爸爸,如果是爸爸就不管了:candidateType.isAssignableFrom(winnerType) 或者winnerType.isAssignableFrom(candidateType)
  • boolean b = A.isAssignableFrom(B) 判断A是否是B的爸爸
addGetMethod
private void addGetMethod(String name, Method method) {
    if (isValidPropertyName(name)) {
      getMethods.put(name, new MethodInvoker(method));
      Type returnType = TypeParameterResolver.resolveReturnType(method, type);
      // 这里调用了
      getTypes.put(name, typeToClass(returnType));
    }
  }
private boolean isValidPropertyName(String name) {
    return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
  }
  • getMethods.put(name, new MethodInvoker(method));
public class MethodInvoker implements Invoker {

  // 传入参数或者传出参数类型
  private final Class<?> type;
  private final Method method;

  /**
   * MethodInvoker构造方法
   * @param method 方法
   */
  public MethodInvoker(Method method) {
    this.method = method;

    if (method.getParameterTypes().length == 1) {
      // 有且只有一个入参时,这里放入入参
      type = method.getParameterTypes()[0];
    } else {
      type = method.getReturnType();
    }
  }

  // 执行函数
  @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;
      }
    }
  }

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

这个类的比较简单,就是存储了方法和方法的入参或者出参参数类型。
当方法的参数只有一个时,存储的是入参参数类型,反之,存储返回的参数类型。

  • invoke()方法就是简单的反射执行当前存储的方法

将参数名称和方法存入对应的getMethods中,继续执行下一步:

  • Type returnType = TypeParameterResolver.resolveReturnType(method, type);
  • method是当前类的方法,type是当前的Class类

要了解这个TypeParamterResolver,需要前置的一点java的Type类的知识,可以参考
Type类相关知识

/**
     * @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
     *         they will be resolved to the actual runtime {@link Type}s.
     */
    public static Type resolveReturnType(Method method, Type srcType) {
        Type returnType = method.getGenericReturnType();
        Class<?> declaringClass = method.getDeclaringClass();
        return resolveType(returnType, srcType, declaringClass);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值