mybatis源码学习------Reflector类

Reflector简介

Reflector反射器,每个Reflector对象对应一个类,该对象会缓存反射操作所需要的类元信息,便于后续反射操作。

Reflector的类注释如下(难得作者心情好,写了两行注释):

This class represents a cached set of class definition information that allows for easy mapping between property names and getter/setter methods.

此类表示一组缓存的类定义信息,可轻松在属性名称和getter / setter方法之间进行映射

Reflector的字段

Reflector的构造函数会对所有字段进行初始化

因为mybatis是一个orm框架,故其对反射使用最多的场景就是将封装了请求参数的pojo拼接到对应的sql语句中,或是将sql查询出的结果集映射到对应的pojo对象中,所以调用pojo的getter和setter方法是最频繁的。观察Reflector类中定义的字段可以发现,Reflector类就是为了更好的服务于ORM映射的使用场景而设计。

private final Class<?> type;//Reflector对应的类
private final String[] readablePropertyNames;//可读属性的名称集合
private final String[] writablePropertyNames;//可写属性的名称集合
private final Map<String, Invoker> setMethods = new HashMap<>();//所有属性的set方法
private final Map<String, Invoker> getMethods = new HashMap<>();//所有属性的get方法
private final Map<String, Class<?>> setTypes = new HashMap<>();//所有set方法的类型
private final Map<String, Class<?>> getTypes = new HashMap<>();//所有get方法的类型
private Constructor<?> defaultConstructor;//默认的构造函数(空参构造)

private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();//不区分大小的属性集合

Reflector的构造函数

构造函数为Reflector类的核心方法,Reflector的构造函数会对上述所有字段进行初始化,便于后续调用,所以本文重点分析构造函数中的逻辑。

构造函数的源码如下:

  public Reflector(Class<?> clazz) {
    type = clazz;
    addDefaultConstructor(clazz);
    addGetMethods(clazz);
    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);
    }
  }

因为构造函数中的逻辑较多,所以简单梳理了如下思维导图,可以对代码的逻辑有一个大致的印象,便于后续阅读。

mybatis源码学习------Reflector类_第1张图片

接下来开始详细分析Reflector类的构造函数源码

设置Class

在Reflector简介的章节中说过,Reflector对象的作用是缓存反射操作所需要的类元信息,便于后续反射操作调用。所以类和Reflector对象是一一对应的关系,故构造函数的第一行代码就是初始化type字段。

addDefaultConstructor

addDefaultConstructor(clazz)方法的作用是找到该类的默认空参构造器。

源码如下:

  private void addDefaultConstructor(Class<?> clazz) {
    //获取该类声明的所有构造函数
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    //找到空参的构造器,并将其赋给defaultConstructor字段
    Arrays.stream(constructors)
        .filter(constructor -> constructor.getParameterTypes().length == 0)
        .findAny()
        .ifPresent(constructor -> this.defaultConstructor = constructor);
  }

对应非lamda代码为(未测试,仅供参考):

  private void addDefaultConstructor(Class<?> clazz) {
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    for (Constructor<?> constructor : constructors) 
        if(constructor.getParameterTypes().length == 0){
            this.defaultConstructor = constructor;
    }
  }

代码逻辑简单,不多BB直接过。

addGetMethods

addGetMethods(clazz)方法的作用找到clazz对应的类中的所有

private void addGetMethods(Class<?> clazz) {
    //因为子类可能会重写父类相同的方法,所以数据结构设计为name->List
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
  	Method[] methods = getClassMethods(clazz);
    //这段lamda的意思是从数组中找到所有参数长度为0,且以get开头的方法,并将其添加到addMethodConflict中
  	Arrays
      	.stream(methods)
      	.filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
    	.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    //解析冲突的get方法	
  	resolveGetterConflicts(conflictingetters);
}

getClassMethods

该方法的作用是从当前类开始,递归向上查找所有父类和接口中定义的方法

private Method[] getClassMethods(Class<?> clazz) {
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    //递归退出条件
    while (currentClass != null && currentClass != Object.class) {
      //将当前类中声明的方法添加到Map集合uniqueMethods中
      addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
      //获取当前类的实现的所有接口
      Class<?>[] interfaces = currentClass.getInterfaces();
      //遍历将当前类所实现的接口中声明的方法添加到Map集合uniqueMethods中
      for (Class<?> anInterface : interfaces) {
        addUniqueMethods(uniqueMethods, anInterface.getMethods());
      }
      //获取当前类的父类,继续向上递归
      currentClass = currentClass.getSuperclass();
    }
    Collection<Method> methods = uniqueMethods.values();
    return methods.toArray(new Method[0]);
}
addUniqueMethods

添加methods数组中的所有方法到map中,其中key是当前方法的签名,mybatis自定义了方法签名的格式,用于区分重写方法和重载方法,自定义方法签名的格式为:

signature = 返回值类型#方法名:参数类型1,参数类型2,参数类型3…

addUniqueMethods方法的逻辑较为简单,不做分析。不过需要注意的是方法并没有将桥接方法添加到map中,关于什么是桥接方法,可以参考文章

[]: https://www.zhihu.com/question/54895701/answer/141623158 “Java反射中method.isBridge()由来,含义和使用场景?”

  private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
      if (!currentMethod.isBridge()) {
        String signature = getSignature(currentMethod);
        if (!uniqueMethods.containsKey(signature)) {
          uniqueMethods.put(signature, currentMethod);
        }
      }
    }
  }

addMethodConflict

1、调用isValidPropertyName方法判断当前属性名称是否是合法的属性名称,比如不能是以$开头,属性名不等于serialVersionUID等等

2、将当前方法添加到conflictingMethods中

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);
  }
}

resolveGetterConflicts

遍历每个属性,查找其最匹配的方法。因为子类可以覆写父类的方法,所以一个属性,可能对应多个 getting 方法。

winner表示胜者,candidate表示候选人,代码逻辑不复杂,直接看注释即可。

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;//是否有二义性
    for (Method candidate : entry.getValue()) {
      //初始情况,直接赋值
      if (winner == null) {
        winner = candidate;
        continue;
      }
      Class<?> winnerType = winner.getReturnType();
      Class<?> candidateType = candidate.getReturnType();
      if (candidateType.equals(winnerType)) {
        //如果候选者的返回值类型和胜者的返回值类型相同,且不是布尔类型,则表示出现了二义性。
        //也就是说在前面的addUniqueMethods()方法中并没有对这两个方法进行合并,这种情况是不允许的。
        //所以将isAmbiguous标志位置为True,在稍后的代码逻辑中会抛出对应的异常(ReflectionException)		//终止程序运行
        if (!boolean.class.equals(candidateType)) {
          isAmbiguous = true;
          break;
        } else if (candidate.getName().startsWith("is")) {
          //如果是boolean类型的话,属性名为is开头的优先级更高
          winner = candidate;
        }
          //下面这段不理解的话可以看下面的例子
      } else if (candidateType.isAssignableFrom(winnerType)) {
          //如果此时candidateType是winnerType的父类,则此时不作操作
      } else if (winnerType.isAssignableFrom(candidateType)) {
          //如果此时winnerType是candidateType的父类,则candidate更适合
        winner = candidate;
      } else {
        isAmbiguous = true;
        break;
      }
    }
    addGetMethod(propName, winner, isAmbiguous);
  }
}

举例

public class Father<T> {
  private T nickname;
  
  public T getNickname() {
    return nickname;
  }

  public void setNickname(T nickname) {
    this.nickname = nickname;
  }
}

public class Son extends Father<String>{
  private String nickname;

  @Override
  public String getNickname() {
    return nickname;
  }
    
  @Override
  public void setNickname(String nickname) {
    this.nickname = nickname;
  }
}

以上述两个类为例,Father类的nickname字段的类型为Object(泛型擦除后),而Son类的nickname字段的类型为String。在出现这个情况时,上述代码会选择子类的该方法

读者可debug下面的测试类进行调试

  @Test
  void testCreateReflactor(){
    ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    Reflector reflector = reflectorFactory.findForClass(Son.class);
    Assertions.assertEquals(String.class, reflector.getGetterType("nickname"));
  }
addGetMethod

最后会调用addGetMethod(String name, Method method, boolean isAmbiguous)方法将选择出的get方法添加到当前Reflector对象的getMethods和getTypes属性中。

代码中的MethodInvoker和TypeParameterResolver.resolveReturnType(method, type)方法会在后续文章中进行详解

private void addGetMethod(String name, Method method, boolean isAmbiguous) {
  //当isAmbiguous为true,即出现二义性时,后续代码会抛出ReflectionException异常,终止程序运行
  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属性中
  getMethods.put(name, invoker);
  //解析get方法的返回值类型
  Type returnType = TypeParameterResolver.resolveReturnType(method, type);
  //添加到getTypes属性中
  getTypes.put(name, typeToClass(returnType));
}

addGetMethods方法逻辑梳理

mybatis源码学习------Reflector类_第2张图片

addSetMethods

addSetMethods方法的逻辑与addGetMethods方法的逻辑相似,差异点在于resolveSetterConflicts方法,即如何选择出合适的set方法,所以本节直接分析resolveSetterConflicts方法即可,其余部分略略略。。。

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);
}

resolveSetterConflicts

该方法借助了getTypes和getMethods进行判断,逻辑简单,直接看注释就好

  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    for (Entry<String, List<Method>> entry : conflictingSetters.entrySet()) {
      String propName = entry.getKey();
      List<Method> setters = entry.getValue();
      Class<?> getterType = getTypes.get(propName);
      boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;
      boolean isSetterAmbiguous = false;
      Method match = null;
      for (Method setter : setters) {
        //getter方法返回的类型和当前setter方法的入参类型相同,即找到最合适的set方法
        if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {
          match = setter;
          break;
        }
        //没有找到对应的get方法,且set方法不存在二义性,处理只有set方法没有get方法的情况
        if (!isSetterAmbiguous) {
          match = pickBetterSetter(match, setter, propName);
          isSetterAmbiguous = match == null;
        }
      }
      //如果找到合适的set方法,则将其添加到setMethods和setTypes属性中
      if (match != null) {
        addSetMethod(propName, match);
      }
    }
  }
pickBetterSetter

与resolveGetterConflicts方法相关的逻辑大同小异

  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;
  }

addFields

递归添加类及其父类中定义的字段,关于字段和属性的区别可以参考这篇文章

private void addFields(Class<?> clazz) {
  //获取clazz声明的所有字段
  Field[] fields = clazz.getDeclaredFields();
  for (Field field : fields) {
    if (!setMethods.containsKey(field.getName())) {
      int modifiers = field.getModifiers();//获取字段的修饰符
      //不是final,也不是static的将会被添加到setMethods和setTypes字段中
      if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
        addSetField(field);
      }
    }
    //添加到getMethods和getTypes字段中
    if (!getMethods.containsKey(field.getName())) {
      addGetField(field);
    }
  }
  if (clazz.getSuperclass() != null) {
    addFields(clazz.getSuperclass());
  }
}

readablePropertyNames和writablePropertyNames属性的初始化

readablePropertyNames数组中保存着所有可读属性和字段的名称,即定义了get方法的字段名

writablePropertyNames数组中保存着所有可写属性和字段的名称,即定义了set方法的字段名

ReflectorFactory接口

通过上面的介绍可以知道,在mybatis中,一个类就会对应一个Reflector对象。假设有100个类,就需要有100个对应的Reflector对象,如何管理这些Reflector对象是需要考虑的问题,于是mybatis的作者设计了ReflectorFactory接口并对其进行了默认实现,便于上层代码使用。

ReflectorFactory为Reflector的工厂接口,用于创建和缓存 Reflector 对象,其接口定义如下:

public interface ReflectorFactory {
 	//判断是否允许对reflector进行缓存
	boolean isClassCacheEnabled();
	//设置是否允许对reflector进行缓存
  	void setClassCacheEnabled(boolean classCacheEnabled);
	//获取type对应的Reflector对象
  	Reflector findForClass(Class<?> type);
}

DefaultReflectorFactory

ReflectorFactory接口只有一个实现类,即DefaultReflectorFactory类

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;
  }
  //如果允许缓存则直接在类中定义的ConcurrentHashMap中获取
  //如果不允许缓存则每次都会创建新的Reflector对象
  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
      //如果reflectorMap中没有对应的Reflector对象,则会创建一个对象并将其放在reflectorMap中
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      return new Reflector(type);
    }
  }
}

你可能感兴趣的:(源码学习,mybatis,源码,reflector,反射)