MyBatis源码分析-1-基础支持层-反射模块-Reflector/ReflectorFactory

本文主要介绍MyBatis的反射模块是如何实现的。

MyBatis 反射的核心类Reflector,下面我先说明它的构造函数和成员变量。具体方法下面详解。

org.apache.ibatis.reflection.Reflector
public class Reflector {

  private final Class<?> type; //对应的Class 类型
  //可读属性的名称集合,可读属性就是存在相应getter 方法的属性,初始值为空数纽
  private final String[] readablePropertyNames;
  //可写属性的名称集合,可写属性就是存在相应setter 方法的属性,初始值为空数纽
  private final String[] writeablePropertyNames;
  //记录了属性相应的setter 方法, key 是属性名称, value 是Invoker 对象,它是对setter 方法对应
  private final Map<String, Invoker> setMethods = new HashMap<>();
  //记录了属性相应的getter 方法, key 是属性名称, value 是Invoker 对象,它是对setter 方法对应
  private final Map<String, Invoker> getMethods = new HashMap<>();
  //记录了属性相应的setter 方法的参数值类型, ke y 是属性名称, value 是setter 方法的参数类型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  //记录了属性相应的getter 方法的返回位类型, key 是属性名称, value 是getter 方法的返回位类型
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  //记录了默认构造方法
  private Constructor<?> defaultConstructor;
  //记录了所有属性名称的集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

  public Reflector(Class<?> clazz) {
    type = clazz;
    //查找clazz的无参构造方法,通过反射遍历所有构造方法,找到构造参数集合长度为0的。
    addDefaultConstructor(clazz);
    //处理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合
    addGetMethods(clazz);
    //处理clazz 中的set ter 方法,填充setMethods 集合和set Types 集合
    addSetMethods(clazz);
    //处理没有get/set的方法字段
    addFields(clazz);
    //初始化可读写的名称集合
    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);
    }
  }
    。。。。。。。。。。。。具体代码先忽略,通过构造函数的调用慢慢渗透。
}

1:addDefaultConstructor() // 查找clazz的无参构造方法,通过反射遍历所有构造方法,找到构造参数集合长度为0的
主要实现的思想是,通过 clazz.getDeclaredConstructors();获取所有构造方法集合,然后循环遍历 判断参数长度为0的,并且构造函数权限可控制的设为默认构造方法。

private void addDefaultConstructor(Class<?> clazz) {
  Constructor<?>[] consts = clazz.getDeclaredConstructors();
  for (Constructor<?> constructor : consts) {
    if (constructor.getParameterTypes().length == 0) {
    //判断反射对象的控制权限 为true是可控制
      if (canControlMemberAccessible()) {
        try {
        //设置Accessible为true后,反射可以访问私有变量。
          constructor.setAccessible(true);
        } catch (Exception e) {
          // Ignored. This is only a final precaution, nothing we can do.
        }
      }
      if (constructor.isAccessible()) {
        this.defaultConstructor = constructor;
      }
    }
  }
}

2:addGetMethods(clazz)// 处理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合

private void addGetMethods(Class<?> cls) {
  Map<String, List<Method>> conflictingGetters = new HashMap<>();
  //获取当前类以及父类中定义的所有方法的唯一签名以及相应的Method对象。
  Method[] methods = getClassMethods(cls);
  for (Method method : methods) {
    if (method.getParameterTypes().length > 0) {
      continue;
    }
    String name = method.getName();
    //判断如果方法明是以get开头并且方法名长度大于3 或者 方法名是以is开头并且长度大于2
    if ((name.startsWith("get") && name.length() > 3)
        || (name.startsWith("is") && name.length() > 2)) {
      //将方法名截取,如果是is从第二位截取,如果是get或者set从第三位开始截取
      name = PropertyNamer.methodToProperty(name);
       
       //addMethodConflict 方法内部很简单只有两行代码:
       //1:List<Method> list=conflictingGetters.computeIfAbsent(name,K->new ArrayList<>());  这句话的意思是,在conflictingGetters 的Map中 如果key中存在name,name什么都不做,将value返回,如果name不存在,则返回一个新的ArrayList.
       //2:list.add(method); 将方法对象添加到list对象中。
      addMethodConflict(conflictingGetters, name, method);
    }
  }
  resolveGetterConflicts(conflictingGetters);
}

    2-1:getClassMethods(cls);//获取当前类以及父类中定义的所有方法的唯一签名以及相应的Method对象。

private Method[] getClassMethods(Class<?> cls) {
  Map<String, Method> uniqueMethods = new HashMap<>();
  Class<?> currentClass = cls;
  while (currentClass != null && currentClass != Object.class) {
    //currentClass.getDeclaredMethods(),获取当前类的所有方法
    //addUniqueMethods 为每个方法生成唯一签名,并记录到uniqueMethods集合中
    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[methods.size()]);
}

        2-1-1: addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); 为每个方法生成唯一签名,并记录到uniqueMethods集合中

private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
  for (Method currentMethod : methods) {
    //判断是不是桥接方法, 桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法 
    if (!currentMethod.isBridge()) {
      //获取签名 
      // 签名格式为:方法返回参数#方法名:参数名     ps:多个参数用,分割  签名样例:String#getName:User
      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
      //如果签名存在,则不做处理,表示子类已经覆盖了该方法。
      //如果签名不存在,则将签名作为Key,Method作为value 添加到uniqueMethods中
      if (!uniqueMethods.containsKey(signature)) {
        if (canControlMemberAccessible()) {
          try {
            currentMethod.setAccessible(true);
          } catch (Exception e) {
            // Ignored. This is only a final precaution, nothing we can do.
          }
        }

        uniqueMethods.put(signature, currentMethod);
      }
    }
  }
}

    2-2: resolveGetterConflicts(conflictingGetters);;//在2-1中返回的方法可能存在,两个相同的方法名称,因为当子类实现父类方法时且参数不同,此时2-1生成的签名是不同的生成签名的规则是 方法返回值#方法名#参数名,那么就会返回两个相同的方法名。 resolveGetterConflicts方法会对这种覆写的情况进行处理,同时将处理后的getter方法记录到getMethods集合中,将其返回值类型填充到getTypes集合中。 内部实现主要是两个for循环,循环比较方法名称相同的情况下,返回值不同的情况下,拿第二个当最终想要的Method。

private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
  for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
    Method winner = null;
    String propName = entry.getKey();
    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)) {
          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")) {
          winner = candidate;
        }
      } else if (candidateType.isAssignableFrom(winnerType)) {
        // OK getter type is descendant
      } else if (winnerType.isAssignableFrom(candidateType)) {
        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.");
      }
    }
    addGetMethod(propName, winner);
  }
}

总结一下addGetMethods(clazz)方法 addSetMethods(clazz)大致相同:

首先创建:

Map<String, List<Method>> conflictingGetters = new HashMap<>();

1:获取子类和父类的所有方法。 获取方法是 先生成唯一签名,唯一签名规则是方法返回值#方法名:方法参数1,方法参数2 。 根据签名作为key,method对象作为value生成Map,通过签名进行过滤,将此M,ap转换为List返回。

2:循环遍历Map,找到符合条件的方法名,is开头或者get开头的,将方法名截取,截取后的方法名作为key,List<Method>作为value,放入到conflictingGetters中。

3:由于子类存在实现父类方法,且返回值不同的情况,导致用一方法名 可能有不同的Method ,第三步 resolveGetterConflicts方法会对这种覆写的情况进行处理,同时将处理后的getter方法记录到getMethods集合中,将其返回值类型填充到getTypes集合中。

Reflector Factory 接口主要实现了对Reflector 对象的创建和缓存,有三个方法:该接口定义如下:

public interface ReflectorFactory {

  boolean isClassCacheEnabled(); //检测该ReflectorFactory对象是否会缓存Reflector对象

  void setClassCacheEnabled(boolean classCacheEnabled);//设置是否缓存Reflector对象

  Reflector findForClass(Class<?> type); //创建指定class对应的Reflector对象
}

Reflector Factory的实现是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;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    //如果开启缓存,Reflector对象从ConcurrentMap<Class<?>, Reflector> 取出。
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {//没开启缓存,重新创建。
      return new Reflector(type);
    }
  }

}

DefaultReflectorFactory 的缓存是通过ConcurrentMap来实现的,如果开启了缓存,那么就从ConcurrentMap取Reflector,如果没有开启,就新建Reflector.

除了使用MyBatis 提供的DefaultReflectorFactory 实现,我们还可以在mybatis-config .xml中配置自定义的ReflectorFactory 实现类,从而实现功能上的扩展。

猜你喜欢

转载自my.oschina.net/u/3905482/blog/2249009
今日推荐