2.4.3.实际缓存类反射数据的 Reflector 对象
Reflector
是mybatis中定义的一个用于描述类定义信息的对象,它缓存了指定对象的类型,可读/可写属性,getter
/setter
方法,以及构造器等信息,并提供了操作这些属性或方法的入口,有效的简化针对指定对象的方法和属性的反射操作。
Reflector
对象的定义和实现涉及到的东西比较多,我们先简单了解一下这个类的属性定义:
public class Reflector {
/**
* 被封装的java对象的类型
*/
private final Class<?> type;
/**
* {@link #type}的可读属性名称集合
*/
private final String[] readablePropertyNames;
/**
* {@link #type}的可写属性名称集合
*/
private final String[] writeablePropertyNames;
/**
* {@link #type}的set方法和执行器的映射列表
*/
private final Map<String, Invoker> setMethods = new HashMap<>();
/**
* {@link #type}get方法集合和执行器的映射列表
*/
private final Map<String, Invoker> getMethods = new HashMap<>();
/**
* set字段类型
* 字段名称=>字段类型
*/
private final Map<String, Class<?>> setTypes = new HashMap<>();
/**
* get字段类型
* 字段名称=>字段类型
*/
private final Map<String, Class<?>> getTypes = new HashMap<>();
/**
* {@link #type}的默认构造函数
*/
private Constructor<?> defaultConstructor;
/**
* {@link #type}的大小写不敏感的属性集合,全大写属性名->属性名
*/
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
...
}
这些属性的作用已经被简单注释出来,有关他们的赋值操作和更详细的说明会在解析Reflector
对象的构造方法时给出。
Reflector
对象的构造过程相对比较复杂,在构造时做了很多额外的操作,我们先整体看一下他的构造实现,之后再深入到内部依赖的各个子方法中了解详细的实现过程:
public Reflector(Class<?> clazz) {
// 被封装的JAVA类型
type = clazz;
// 配置默认构造方法
addDefaultConstructor(clazz);
// 配置以get/is开头的方法集合
addGetMethods(clazz);
// 配置set方法
addSetMethods(clazz);
// 添加字段集合
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);
}
}
简单的看过构造方法的代码之后,我们大概也能猜出来内部方法的实现,整个实现无外乎依赖于反射机制,获取到传进来的java类的属性,方法,构造器这些东西,然后处理处理,把他们保存起来,便于之后的使用。
这个东西很好理解,就是操作有点繁琐。
整个构造过程,大体上可以分为两个阶段:
第一个阶段是解析
Class<?> clazz
对象,获取Class<?> clazz
类中的基本信息(属性和方法)。// 被封装的JAVA类型 type = clazz; // 配置默认构造方法 addDefaultConstructor(clazz); // 配置以get/is开头的方法集合 addGetMethods(clazz); // 配置set方法 addSetMethods(clazz); // 添加字段集合 addFields(clazz);
第二个阶段是第一阶段的补充,使用第一阶段获取到的数据完善
Reflector
对象中的其他属性。// 配置可读字段名称集合 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); }
在第一个阶段中,Reflector
会按照顺序依次处理传入的clazz
对象的构造方法,getter
/setter
方法,以及属性。
1.处理构造方法
获取
clazz
的无参构造方法赋给defaultConstructor
属性。2.处理
getter
/setter
方法按照
Java Bean
对getter
/setter
方法的定义,筛选出其中有效的方法,并反向解析出这些方法对应的属性名称,之后:- 将方法包装成
Invoker
接口的实例对象,保存到getMethods
/setMethods
集合中 - 移除方法返回对象的泛型,转换成对应的实际类型,保存到
getTypes
/setTypes
集合中。
- 将方法包装成
3.处理属性
对属性的处理操作,是对上一步处理结果的补充,因为在
clazz
中有些属性可能没有定义getter
/setter
方法,所以在上一步中可能就会遗漏掉这部分属性,在这一步针对这些属性:Reflector
会基于反射为他们生成用于执行getter
/setter
操作的Invoker
接口的实例对象,保存到getMethods
/setMethods
集合中。Invoker
接口的作用是统一基于反射处理方法/属性的调用方式,具体的作用在后面会给出。移除属性的泛型,转换成对应的实际类型,保存到
getTypes
/setTypes
集合中。
在第二个阶段中,Reflector
会将getMethods
/setMethods
中所有可读/可写
属性的名称保存到readablePropertyNames
/writeablePropertyNames
集合中, 之后,将这些可读/可写
的属性名称全部大写保存到caseInsensitivePropertyMap
集合中,便于后续的使用。
等这两个阶段完成了,Reflector
对象的构造过程也就完成了。
大概了解了构造方法做的事情之后,我们来看一下具体的实现:
首先,Reflector
肯定是保存一下传进来的类,把他作为一个实例变量,便于后续使用:
// 被缓存的JAVA类型
type = clazz;
接下来是第一阶段的第一步,获取该类的默认构造方法赋值给defaultConstructor
属性:
// 配置默认构造方法
addDefaultConstructor(clazz);
默认构造方法的筛选机制比较简单,获取无参构造方法即可。
private void addDefaultConstructor(Class<?> clazz) {
// 获取所有的构造方法
Constructor<?>[] consts = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : consts) {
if (constructor.getParameterTypes().length == 0) {
// 获取无参构造
this.defaultConstructor = constructor;
}
}
}
然后是第一阶段的第二步,依次处理getter
、setter
方法,getter
、setter
方法的处理操作分别由addGetMethods
和addSetMethods
两个方法来完成:
// 配置以get/is开头的方法集合
addGetMethods(clazz);
// 配置set方法
addSetMethods(clazz);
其中用于处理getter
方法的addGetMethods
,涉及到的操作比较多,其中主要包括了:
- 获取指定类及其父类/接口中的所有方法定义,并筛选出其中有效的
getter
方法 - 根据
Java Bean
规范使用getter
方法的方法名获取到该方法对应的属性名称 - 根据
Java Bean
规范为上一步获取到的属性名称筛选唯一的getter
方法实现 - 将
getter
方法的返回类型的泛型参数定义转换为实际的类定义,比如将List<T>
转换为List<String>
- 保存方法和属性
private void addGetMethods(Class<?> cls) {
// 用于解决方法冲突,方法的重载和重写都会导致一个属性对应多个方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
// 获取被处理的类的所有方法
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
// 忽略所有有入参的getter方法
if (method.getParameterTypes().length > 0) {
continue;
}
String name = method.getName();
// 获取名称已get/is开头,且不是get/is的方法。
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
// 将方法转换为属性名称
name = PropertyNamer.methodToProperty(name);
// 记录每一个属性名称对应的方法集合
addMethodConflict(conflictingGetters, name, method);
}
}
// 处理方法冲突
resolveGetterConflicts(conflictingGetters);
}
整个getter
方法的处理流程还是挺长的,考虑到阅读的连贯性,先了解一下负责获取指定类及其父类/接口中的所有方法定义的getClassMethods
方法,会获取指定类的哪些方法,排序掉哪些方法。
getClassMethods
方法其实是对Class.getMethods()
方法的一个增强实现,他不仅会获取指定类中定义的方法,还会获取指定类实现/继承
的父类/接口
中定义的方法。
/**
* Class.getMethods()方法的增强实现,不仅会获取指定类的方法,还会获取指定类实现/继承的父类和接口中定义的方法。
*
* @param cls 指定类
* @return 所有方法
*/
private Method[] getClassMethods(Class<?> cls) {
// 去重后的方法集合
Map<String, Method> uniqueMethods = new HashMap<>();
// 当前正在处理的java类
Class<?> currentClass = cls;
while (currentClass != null && currentClass != Object.class) {
// currentClass非空,且不是Object类型
// 缓存器所有方法
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()]);
}
在该方法中,首先定义了一个Map<String,Method>
类型的参数uniqueMethods
,从属性名不难猜出,该属性用于存放去重后的方法集合。
他的key
值是根据方法返回值类型,方法名称以及方法入参类型生成的一个String
类型的签名,value
则存放着对应的方法本身。
我们可以先看一下方法签名的计算方法,实现还是比较简单的:
/**
* 为指定的方法生成签名
*
* @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++) {
if (i == 0) {
sb.append(':');
} else {
sb.append(',');
}
sb.append(parameters[i].getName());
}
return sb.toString();
}
因为方法的签名是根据方法返回值、名称、入参类型来生成的,因此如果子类重写父类的方法,子类方法和父类方法的签名将会是一致的,对于这种场景,mybatis会保留子类的方法实现。
在定义了uniqueMethods
属性之后,mybaits接下来会递归处理当前类及其父类和接口,取出其中声明的所有方法,计算出方法签名,并存放到uniqueMethods
集合内。
!!需要注意的是,如果当前被处理的类有父类定义且父类定义不是Object
,那么整个获取当前类及其父类和接口,取出其中声明的所有方法,计算出方法签名,并存放到uniqueMethods
集合内这一步骤,会被递归执行,直到当前被处理的父类声明为空,或者当前被处理类的父类声明为Object
。
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);
}
}
}
}
在保存方法的过程中,mybatis会跳过对JDK自动生成的桥接方法的处理,为符合要求的方法生成一个签名(签名的计算规则前面已经讲过),之后在uniqueMethods
中不存在该方法签名的前提下,保存该方法。
这样,我们通过getClassMethods
方法就可以获取到指定类及其父类和接口中定义的所有方法,这里的所有方法指排除掉被子类重写的方法后的方法集合。
了解完getClassMethods
会获取一个类的哪些方法之后,我们再继续看addGetMethods
方法。
在addGetMethods
方法中,定义了一个Map<String, List<Method>>
类型的conflictingGetters
属性,conflictingGetters
的key值用于存放getter
方法对应的属性名称,value值则存放该属性名称对应的getter
方法集合。
// 用于解决方法冲突,方法的重载和重写都会导致一个属性对应多个方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
为什么会出现一个属性名称对应多个getter方法的场景呢? 这是因为在
Reflector
中,属性名的生成仅仅是依据方法名来完成的, 因为方法的重写和重载都会导致出现多个同名方法,进而就会出现一个属性名对应多个方法的场景, 同时依据Java Bean
的规范,boolean类型的属性可能有get
/is
两种getter方法,也会出现一个属性名对应多个方法的场景。
在声明了conflictingGetters
属性之后,mybatis调用getClassMethods
方法获取到当前java对象中获取到需要被处理的方法集合之后。
// 获取所有方法,该方法会获取指定类实现/继承的父类/接口中的方法
Method[] methods = getClassMethods(cls);
之后,Reflector
会依次处理集合中每个方法,因为在这里我们需要处理的是getter
类型的方法,所以会忽略掉有入参的方法,同时按照Java Bean
规范,只处理以get
/is
开头且方法名不完全是get
/is
的方法。
因为忽略掉了有入参的方法,所以理论上来讲,就排除掉了多个重载方法同时出现在方法集合内的场景,因此在该方法集合内,每个方法名,只会对应一个方法。
for (Method method : methods) {
// 忽略所有有入参的getter方法
if (method.getParameterTypes().length > 0) {
continue;
}
String name = method.getName();
// 获取名称已get/is开头,且不是get/is的方法。
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
// 将方法转换为属性名称
name = PropertyNamer.methodToProperty(name);
// 记录每一个属性名称对应的方法集合,生成用于解决方法冲突的集合
addMethodConflict(conflictingGetters, name, method);
}
}
具体到每个方法的处理细节上,Reflector
首先借助一个叫做PropertyNamer
的类将方法名称按照Java Bean
规范反向解析出属性名,然后调用addMethodConflict
方法,保存属性名和方法之间的映射关系,PropertyNamer
这个类我们待会再说。
addMethodConflict
方法的作用就是往前面定义的conflictingGetters
集合中填充属性名和getter
方法的映射关系。
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
// 如果不存在指定属性名对应的方法列表,则新建一个方法列表
List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
// 保存方法
list.add(method);
}
完成了上述操作后,Reflector
就会调用resolveGetterConflicts
方法来处理conflictingMethods
集合,为每个属性选择一个有效的、最符合Java Bean
规范的最优getter
方法:
// 处理方法冲突
resolveGetterConflicts(conflictingGetters);
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)) {
// 两个方法返回类型一致,但是不是boolean类型
if (!boolean.class.equals(candidateType)) {
// 这里单独拆出来boolean类型,是因为在javabean规范中,他可以同时有`get*()`和`is*()`方法。
// 非boolean类型的属性同时有了两个getter方法,
// 比如name属性同时包含了`getName()`和`isName()`这两个方法。
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")) {
// 针对boolean类型的属性,如果同时有`get*()`和`is*()`方法优先选择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.");
}
}
// 保存getter方法
addGetMethod(propName, winner);
}
}
上面的方法看起来很长,实际上不算复杂,最优getter
方法计算规则如下:
如果属性对应的多个方法返回类型一致,表示该方法同时有
get
和is
开头的两个方法,但是除了boolean
类型的属性优先使用is
开头的方法,其余类型的属性都不允许有两种getter
方法。如果属性对应的多个方法返回类型不一致,那么多个方法返回值之间必须存在继承关系,此时就尽可能选择返回值类型更精确的的方法作为最优方法。
在我们获取到属性名对应的最优getter
方法之后,Reflector
就会调用addGetMethod
方法,来完成保存属性和方法的操作。
// 保存getter方法
addGetMethod(propName, winner);
private void addGetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
// 保存到getter方法中
getMethods.put(name, new MethodInvoker(method));
// 获取方法的返回类型(移除泛型转换为实际类型)
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 保存属性及其对应的java类型的关系
getTypes.put(name, typeToClass(returnType));
}
}
在addGetMethod
方法中只会处理合法的属性名和方法,属性名是否合法的判断操作是由isValidPropertyName
方法来完成的.
/**
* 校验是否为有效的属性名称
*
* @param name 属性名称
*/
private boolean isValidPropertyName(String name) {
return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
}
针对属性名合法的数据,mybatis会把前面获取到的最优getter
方法包装成MethodInvoker
类型的Invoker
实例, 保存到getMethods
集合中。
// 保存到getter方法中
getMethods.put(name, new MethodInvoker(method));
通过TypeParameterResolver
的resolveReturnType
方法将方法返回值类型的泛型转换为实际类型,之后将获取到实际类型通过typeToClass
方法由Type
转换为Class
类型之后再放入到getTypes
集合中。
// 获取方法的返回类型(移除泛型转换为实际类型)
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 保存属性及其对应的java类型的关系
getTypes.put(name, typeToClass(returnType));
MethodInvoker
以及TypeParameterResolver
的解析,在后面都会陆续给出。我们现在只需要简单的了解一下
MethodInvoker
的作用是简化方法的反射操作,TypeParameterResolver
的resolveReturnType
方法的作用是移除方法返回值的泛型定义,将其转化为实际类型就可以了。
在addGetMethod
方法中,我们调用了typeToClass
方法:
// 保存属性及其对应的java类型的关系
getTypes.put(name, typeToClass(returnType));
typeToClass
方法的作用是将Type
类型转换为Class
类型。
private Class<?> typeToClass(Type src) {
Class<?> result = null;
if (src instanceof Class) {
// 普通对象
result = (Class<?>) src;
} else if (src instanceof ParameterizedType) {
// 处理参数化泛型,获取泛型所属类型,例如:Map<K,V> 得到Map。
result = (Class<?>) ((ParameterizedType) src).getRawType();
} else if (src instanceof GenericArrayType) {
// 获取泛型数组中元素的类型
Type componentType = ((GenericArrayType) src).getGenericComponentType();
if (componentType instanceof Class) {
// 泛型数组中元素类型是Class
result = Array.newInstance((Class<?>) componentType, 0).getClass();
} else {
// 泛型数组中元素类型还是泛型
// 递归处理
Class<?> componentClass = typeToClass(componentType);
result = Array.newInstance(componentClass, 0).getClass();
}
}
if (result == null) {
// src是一个TypeVariable或者WildcardType
result = Object.class;
}
return result;
}
整个解析操作的逻辑比较简单,针对Type
类型的src
属性,他的处理逻辑如下:
- 如果
src
就是一个普通的Class
,直接返回该类型即可, - 如果
src
是一个ParameterizedType
(参数化泛型),获取该参数化泛型所属类型,例如:泛型K
的定义是List<K>
,那么就得到List
。 - 如果
Type
是一个GenericArrayType
(泛型数组),获取该数组元素的泛型定义,如果不是泛型直接返回元素类型即可,如果是泛型则递归调用typeToClass
方法。 - 如果
src
不满足上面任何一种条件,则返回Object.class
.
到这里Reflector
对于getter
方法的处理就完成了。
接下来Reflector
会继续处理指定的类中的setter
方法,并添加相应的属性到setMethods
和setTypes
中。
Reflector
对于setter
方法的处理和getter
方法的处理大致是相同的,他的实现依赖于addSetMethods
方法。
// 配置set方法
addSetMethods(clazz);
在addSetMethods
方法中,Reflector
同样定义了一个用于保存属性名和其对应方法的集合,只不过这次命名为conflictingSetters
,同样调用了getClassMethods
方法获取所需处理的所有方法。
// 因为重载而产生冲突的setter方法集合
Map<String, List<Method>> conflictingSetters = new HashMap<>();
// 获取需要处理的所有方法
Method[] methods = getClassMethods(cls);
之后,Reflector
会依次处理集合中每个方法,因为在这里我们需要处理的是setter
类型的方法,所以要求方法必须有且仅有一个入参定义,同时按照Java Bean
规范,只处理以set
开头且方法名不完全是set
的方法。
具体到每个方法的处理细节上,Reflector
依然借助PropertyNamer
将方法名称按照Java Bean
规范反向解析出对应的属性名,然后调用addMethodConflict
方法,保存属性名和方法之间的映射关系。
在这里,addMethodConflict
方法的作用是往前面定义的conflictingSetters
集合中填充属性名和setter
方法的映射关系。
// 因为重载而产生冲突的setter方法集合
Map<String, List<Method>> conflictingSetters = new HashMap<>();
// 获取需要处理的所有方法
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
String name = method.getName();
// 以set开头,且有且只有一个入参
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
// 获取对应的属性名称
name = PropertyNamer.methodToProperty(name);
// 保存方法
addMethodConflict(conflictingSetters, name, method);
}
}
}
// 处理setter方法冲突
resolveSetterConflicts(conflictingSetters);
完成了上述操作后,Reflector
这次会调用resolveSetterConflicts
方法来处理conflictingSetters
集合,为每个属性选择一个有效的、最符合Java Bean
规范的最优setter
方法:
resolveSetterConflicts
方法和resolveGetterConflicts
方法在实现上有很大的区别。
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
for (String propName : conflictingSetters.keySet()) {
// 处理每个属性对应的setter方法集合
// 获取属性对应的setter方法集合
List<Method> setters = conflictingSetters.get(propName);
// 尝试从getTypes中获取到属性对应的类型
Class<?> getterType = getTypes.get(propName);
// 最终匹配的方法
Method match = null;
ReflectionException exception = null;
for (Method setter : setters) {
// 获取入参类型
Class<?> paramType = setter.getParameterTypes()[0];
if (paramType.equals(getterType)) {
// should be the best match
// 方法入参类型和属性类型一致,那他就是最佳选型
match = setter;
break;
}
if (exception == null) {
try {
// 尝试获取两个方法中更好的setter方法
match = pickBetterSetter(match, setter, propName);
} catch (ReflectionException e) {
// there could still be the 'best match'
match = null;
exception = e;
}
}
}
if (match == null) {
throw exception;
} else {
// 保存setter方法
addSetMethod(propName, match);
}
}
}
在resolveSetterConflicts
方法中,会依次处理每个属性定义:
针对具体属性的处理操作,Reflector
会先获取到属性对应的setter
方法集合,并尝试从这个setter
方法集合中选择一个最优的setter
方法用于后续的保存操作。
在筛选最优的setter
方法的过程中,Reflector
会先尝试从getTypes
集合中获取该属性对应的java类型(如果该属性有getter
方法的话,它对应的java类型会在上一步的addGetMethods
方法中被添加到getTypes
集合中)。
如果得到了对应的java类型,我们会用这个java类型和每个setter
方法的入参进行对比,如果二者一致,那么这个setter
方法就是我们需要的最优setter
方法。
如果所有的setter
方法的入参都不能匹配这个java类型,Reflector
就会把所有的setter
方法交给pickBetterSetter
方法进行两两对比,选出方法入参更精准的那个方法作为最佳setter
方法。
如果在对比的过程中发现两个setter
方法的入参类型之间没有任何关系,Reflector
就无法选择最佳setter
方法,所以就会触发异常。
/**
* 该方法的作用是为指定的属性选择一个更好的setter方法定义。
* setter方法的选择取决于两个setter方法的入参的关系,如果两个方法的入参类型没有关联,将会导致异常。
* 否则则会获取两个入参类型中相对更精确地方法。
*
* @param setter1 第一种setter方法
* @param setter2 第二种setter方法
* @param property 属性名
* @return 最佳setter方法
*/
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;
}
throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '"
+ setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '"
+ paramType2.getName() + "'.");
}
当我们获取到了最佳setter
方法之后,Reflector
就会委托addSetMethod
方法把属性和最佳setter
方法保存到setMethods
和setTypes
中。
private void addSetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
// 保存属性和setter方法执行器的关系
setMethods.put(name, new MethodInvoker(method));
// 获取方法实际入参类型(移除泛型转换为实际类型)
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
// 保存属性及其setter方法入参类型关系
setTypes.put(name, typeToClass(paramTypes[0]));
}
}
addSetMethod
方法的处理和addGetMethod
方法类似,他同样依赖isValidPropertyName
方法来筛选出需要处理的合法属性名。
针对属性名合法的数据,addSetMethod
方法同样会把前面获取到的最优setter
方法包装成MethodInvoker
类型的Invoker
实例, 然后保存到setMethods
集合中。
// 保存属性和setter方法执行器的关系
setMethods.put(name, new MethodInvoker(method));
区别在于,对于泛型的处理,addSetMethod
是 通过TypeParameterResolver
的resolveParamTypes
方法将方法入参类型的泛型转换为实际类型,之后将获取到的类型通过typeToClass
由Type
转换为Class
类型放入到setTypes
集合中。
到这里,对于setter
方法的处理也完成了,接下来就是第一阶段的第三步,处理指定类的属性定义。
Reflector
对于指定类属性的处理操作是由addFields
方法来完成的。
// 添加字段集合
addFields(clazz);
addFields
方法其实是对addGetMethods
方法和addSetMethods
方法的补充,因为在clazz
中有些属性可能没有定义getter
/setter
方法,所以在addGetMethods
方法和addSetMethods
方法中可能就会遗漏掉这部分属性的处理。
private void addFields(Class<?> clazz) {
// 获取所有字段定义
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 只处理没有getter/setter方法的属性
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))) {
// 非静态常量
addSetField(field);
}
}
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
if (clazz.getSuperclass() != null) {
// 递归处理父类
addFields(clazz.getSuperclass());
}
}
addFields
方法就是为了处理这些被遗漏掉的属性:
在该方法的具体实现上,Reflector
会先获取到clazz
的所有属性定义:
// 获取所有字段定义
Field[] fields = clazz.getDeclaredFields();
之后针对每个具体的属性,分别进行setter
方法和getter
方法的处理。
我们之前说过addFields
方法是对addGetMethods
方法和addSetMethods
方法实现的补充,所以在处理属性对应的setter
方法时,会要求该属性没有被保存到setMethods
集合中 ,同时要求属性的定义不能是静态常量,因为静态常量的赋值操作只能在类加载器中完成,不能通过反射进行修改。 对于符合要求的属性,Reflector
会将其交给addSetField
方法来完成后续的处理。
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))) {
// 处理非静态常量
addSetField(field);
}
}
在处理属性的getter
方法时,基于同样的原因,会要求该属性没有被保存到getMethods
集合中,对于符合要求的属性,Reflector
会将其交给addGetField
方法来完成后续的处理。
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
addGetField
方法和addSetMethods
方法的实现类似,他们都会先使用isValidPropertyName
方法校验属性名称的合法性,针对合法的属性:
addGetField
方法会把属性包装成GetFieldInvoker
类型的Invoker
实例存放到getMethods
集合中,addSetMethods
方法会把属性包装成SetFieldInvoker
类型的Invoker
实例存放到setMethods
集合中.
之后,两个方法都会通过TypeParameterResolver
的resolveFieldType
将属性定义中泛型转换为实际类型,并通过typeToClass
方法将其由Type
类型转换为Class
类型之后存放到setTypes
或者getTypes
集合中。
private void addSetField(Field field) {
if (isValidPropertyName(field.getName())) {
// 配置字段的setter方法
setMethods.put(field.getName(), new SetFieldInvoker(field));
// 获取该字段类型
Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
// 保存字段名称和字段类型的关系
setTypes.put(field.getName(), typeToClass(fieldType));
}
}
private void addGetField(Field field) {
if (isValidPropertyName(field.getName())) {
// 配置字段getter方法
getMethods.put(field.getName(), new GetFieldInvoker(field));
// 获取该字段的类型
Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
// 保存字段名称和字段类型的关系
getTypes.put(field.getName(), typeToClass(fieldType));
}
在完成当前类中所有的属性的处理操作之后,Reflector
会获取当前clazz
的父类作为参数继续递归调用addFields
方法完成父类中属性定义的处理操作。
当所有父类中的属性都被处理完成之后,Reflector
对象构造方法的第一阶段(解析
Class<?> clazz对象,获取
Class<?> clazz类中的基本信息(属性和方法)
)就已经完成了。
接下来就是依赖前端获取到的数据,完成Reflector
对象中其他属性的赋值操作。
在第二个阶段中,Reflector
会获取getMethods
集合中所有可读属性的名称将其保存到readablePropertyNames
集合中:
// 配置可读字段名称集合
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
获取setMethods
集合中所有可写属性的名称将其保存到writeablePropertyNames
集合中:
// 配置可写字段名称集合
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
之后,将所有属性名称转换为大写,保存到caseInsensitivePropertyMap
集合中:
// 转换为大写,并放入caseInsensitivePropertyMap中,便于后期查找
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
完成了上述属性的赋值操作之后,Reflector
对象的构造方法也就完成了。
在解析Reflector
对象的构造方法时,我们提到过他的实现依赖于Invoker
及其实现类。