一、Reflector简介
Reflector是Mybatis中反射模块的基础,每个Reflector对象都对应一个类,在Reflector中缓存了反射操作需要使用的类的元信息。ReflectorFactory接口主要是为了实现对Reflector对象的创建和缓存,Mybatis为ReflectorFactory接口提供了一个默认实现类DefaultReflectorFactory(如果想扩展实现ReflectorFactory接口即可),其中findForClass()方法实现会为指定的Class创建Reflector对象,并将Reflector 对象缓存到reflectorMap中。
二、Reflector类源码
1、字段
// 对应的Class类型
private final Class<?> type;
// 可读属性的名称集合,可读属性就是存在相应getter方法的属性,初始值为空数纽
private final String[] readablePropertyNames;
// 可写属性的名称集合,可写属性就是存在相应setter方法的属性,初始值为空数纽
private final String[] writeablePropertyNames;
// 属性相应的setter方法,key是属性名称,value是Invoker对象,它是对setter方法对应Method对象的封装
private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
// 属性相应的getter方法集合, key是属性名称, value也是Invoker对象
private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
// 属性相应的setter方法的参数值类型, key是属性名称, value是setter方法的参数类型
private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
// 属性相应的getter方法的返回位类型, key是属性名称, value是getter方法的返回类型
private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
// 默认构造方法
private Constructor<?> defaultConstructor;
// 所有属性名称的集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
2、构造方法
在构造函数中,主要实现了对类的元信息进行解析和缓存,即初始化相关字段数据。下面分别分析数据初始化的过程。其中,type字段就是class。
/**
* 解析指定的Class对象,填充相应的属性
* @param clazz
*/
public Reflector(Class<?> clazz) {
type = clazz;//初始化type字段
// 查找clazz的默认构造方法(无参构造方法),具体实现是通过反射遍历所有构造方法
addDefaultConstructor(clazz);
// 处理clazz中的getter方法,填充getMethods集合和getTypes集合
addGetMethods(clazz);
// 处理clazz中的setter方法,填充setMethods集合和setTypes集合
addSetMethods(clazz);
// 处理没有getter/setter方法的字段
addFields(clazz);
// 根据getMethods/setMethods集合,初始化可读/写属性的名称集合
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
writablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
// 初始化caseinsensitivePropertyMap集合,其中记录了所有大写格式的属性名称
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
3、addDefaultConstructor()初始化默认构造方法
查找clazz的默认构造方法,即无参构造方法,具体实现是通过反射遍历所有构造方法,
然后通过过滤查询到默认构造函数,并赋值给defaultConstructor字段。
private void addDefaultConstructor(Class<?> clazz) {
// 获取自己的构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
// 找到无参构造函数,作为默认构造函数
Arrays.stream(constructors)
.filter(constructor -> constructor.getParameterTypes().length == 0)
.findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
}
4、addGetMethods()
分析addGetMethods()方法,代码如下。其中,包含了getClassMethods()方法,主要用来获取当前类以及其父类中定义的所有方法的唯一签名以及相应的Method对象;addMethodConflict()方法,主要用来获取字段名和字段名对应的getter方法的映射集合,并把结果保存到conflictingGetters变量中;resolveGetterConflicts()方法,主要用来处理重复的方法。注意:一个key会对应多个method的原因是:当子类覆盖了父类的getter方法且返回值发生变化时,会产生两个签名不同的方法。
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
// 获得当前类中的所有方法,包括继承来的和实现的接口方法
Method[] methods = getClassMethods(clazz);
/**
* 从所有方法中筛选出get方法
* 规则是使用PropertyNamer的isGetter方法来判断是否是get方法:方法没有参数并且方法的名称以get或者是is开头
* 然后使用PropertyNamer通过方法名来确定对应的属性,因为获取的方法中可能存在同名的重写与被重写方法,
* 故先解决冲突,于是把所有的getter属性方法添加到临时conflictingGetters冲突集合待解决
*/
Arrays.stream(methods)
.filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
// 解决一个属性对应多个getter的问题
resolveGetterConflicts(conflictingGetters);
}
5、getClassMethods()方法
getClassMethods()方法,通过while循环,加载当前类及其所有父类中对应的Method对象,并把这些Method对象存储到了uniqueMethods对象中,其中唯一签名作为key,Method对象作为value值,即用来获取当前类以及其父类中定义的所有方法的唯一签名以及相应的Method对象。
private Method[] getClassMethods(Class<?> clazz) {
// 记录指定类中定义的全部方法的唯一签名以及对应的Method对象
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
while (currentClass != null && currentClass != Object.class) {
// 记录currentClass这个类中定义的全部方法(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());
}
// 迭代,直到Object类终止循环迭代(获取父类,继续while循环)
currentClass = currentClass.getSuperclass();
}
// 获取所有的method对象,并转换成数组返回
Collection<Method> methods = uniqueMethods.values();
return methods.toArray(new Method[0]);
}
6、addUniqueMethods()方法
addUniqueMethods()方法,主要是为第二个参数, methods数组中的每个Method对象生成唯一签名,井记录到uniqueMethods变量中,根据生成的签名判断,如果已经添加过,就不再重复添加。
注意:虽然添加了重复判断,但是还是可能出现重复记录,因为当子类覆盖了父类的getter方法且返回值发生变化时,会产生两个签名不同的同名方法。
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
// 检测是否在子类中已经添加过该方法,如果在子类中已经添加过, 则表示子类覆盖了该方法,
// 无须再向uniqueMethods集合中添加该方法了
if (!uniqueMethods.containsKey(signature)) {
uniqueMethods.put(signature, currentMethod);
}
}
}
}
7、getSignature()方法
生成方法的签名,得到的方法签名是全局唯一的,可以作为该方法的唯一标识, 格式:返回值类型#方法名:参数类型列表;以private String girl(String boy, Integer age)为例,方法的唯一签名是:java.lang.String#girl:java.lang.String,java.lang.Integer
private String getSignature(Method method) {
// 以private String girl(String boy, Integer age)为例
StringBuilder sb = new StringBuilder(); // 用于拼接签名
Class<?> returnType = method.getReturnType(); // 获取方法返回类型class
System.out.println("######"+returnType);// 打印如:class java.lang.String
if (returnType != null) {
sb.append(returnType.getName()).append('#'); // 返回类型# 如:java.lang.String#
}
sb.append(method.getName()); // 返回类型#方法名 如:java.lang.String#girl
Class<?>[] parameters = method.getParameterTypes(); // 方法入参类型数组class
for (int i = 0; i < parameters.length; i++) {
// 多个入参用“,”隔开,如:java.lang.String#girl:java.lang.String,java.lang.Integer
sb.append(i == 0 ? ':' : ',').append(parameters[i].getName());
}
System.out.println(">>>>>>>"+sb.toString());
return sb.toString();
}
8、resolveGetterConflicts()方法
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;
// 遍历当前属性的冲突方法 winner赢家 candidate候选人
for (Method candidate : entry.getValue()) {
if (winner == null) {
winner = candidate;// 第一个时,默认是winner,跳过当前循环继续和后面的比较
continue;
}
// 分别获得当前获胜方法的返回值类型和当前遍历到的方法的返回值类型
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
if (candidateType.equals(winnerType)) {
// 返回类型相同,并且返回值类型不为boolean,那么标记为歧义
if (!boolean.class.equals(candidateType)) {
isAmbiguous = true;
break;
} else if (candidate.getName().startsWith("is")) {
// 如果返回值类型是boolean,那么方法名以is开头的胜出
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// 当前遍历到的方法的返回值是当前获胜者返回值类型的子类,获胜者不变
// OK getter type is descendant getter 类型是后代
} else if (winnerType.isAssignableFrom(candidateType)) {
winner = candidate;
} else {
// 和上面的情况相反
isAmbiguous = true;
break;
}
}
addGetMethod(propName, winner, isAmbiguous);
}
}
9、resolveSetterConflicts()方法
resolveSetterConflicts()方法,用来把上一步addMethodConflict()方法产生的conflictingGetters数据中重复的Method对象进行处理,变成一个字段对应一个Method对象形式的数据,然后通过addGetMethod()方法把对应key和value存到变量getMethods和getTypes中。