一、简介
Reflector
是整个反射器模块的基础,每个Reflector
对象都对应一个类,在其构造函数中,根据传入的Class对象,调用Java的反射API,获取并缓存反射操作需要用到的这个类的元信息。
二、成员属性
private Class<?> type; // 对应的Class类型
private String[] readablePropertyNames = EMPTY_STRING_ARRAY; // 可读属性的名称集合,可读属性就是存在相应getter方法的属性,初始值为空数组
private String[] writeablePropertyNames = EMPTY_STRING_ARRAY; // 可写属性的名称集合,可写属性就是存在相应setter方法的属性,初始值为空数组
private Map<String, Invoker> setMethods = new HashMap<String, Invoker>(); // 记录了属性相应的setter方法,key是属性名称,value是Invoker对象,其封装了setter方法对应的Method对象
private Map<String, Invoker> getMethods = new HashMap<String, Invoker>(); // 属性相应的getter方法集合,key是属性名称,value是Invoker对象
private Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>(); // 记录了属性相应的setter方法的参数值类型,key是属性名称,value是setter方法的参数类型
private Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>(); // 记录了属性相应的getter方法的返回值类型,key是属性名称,value是getter方法的返回值类型
private Constructor<?> defaultConstructor; // 记录了默认的构造方法
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>(); // 记录了所有属性名称的集合,key统一记录成大写
-
type
:传入的需要解析元信息的类,每个 Reflector 对象都对应一个类。 -
readablePropertyNames
:可读属性的名称集合,一般Java Bean中属性定义为私有,要读取属性值,必须通过 getter 方法来获取,换句话说,假如属性有相应的 getter 方法,我们就认定为该属性为可读属性。假如属性名为 username ,则方法名一般为 getUsername() ,假如属性是布尔值如 open ,则方法名一般为 isOpen() 。 -
writeablePropertyNames
:可写属性的名称集合,同样的类似上面的,如果属性有相应的 setter 方法,可认定为可写属性。 -
setMethods
:实际运行中如将SQL执行返回结果集映射为Java对象,设置对象属性的操作需要反射来完成,这些操作封装在 Invoker 对象中,该对象缓存属性对应的 Field 对象,设置属性值依赖该 Field 对象完成;key对应属性名称,value对应 Invoker 对象。 -
getMethods
:同样获取对象属性值需要反射来完成,原理跟上面类似,不过反射的操作由 set 变成 get 。 -
setTypes
:不管是执行SQL前参数的预编译,还是执行完之后将结果集映射为Java对象,都需要知道属性的类型,底层的JDBC对象才知道要调用哪些相应的方法;本属性缓存了属性名和属性的 setter 方法的映射关系。 -
getTypes
:同上,本属性缓存了属性名和属性的 getter 方法的映射关系。 -
defaultConstructor
:传入解析的类的默认构造方法,常用于通过反射创建Java对象。 -
caseInsensitivePropertyMap
:记录了所有属性名称的集合,key统一处理成大写,value则为属性名。
三、构造方法
Reflector
的构造方法中,会解析执行的Class对象,并填充上述集合,源码如下:
public Reflector(Class<?> clazz) {
type = clazz; // 初始化type字段
addDefaultConstructor(clazz); // 查找clazz的默认构造方法(无参构造方法),通过反射获取所有构造方法,通过遍历找出参数长度为0的构造方法
addGetMethods(clazz); // 获取clazz中的getter方法,并填充getMethods集合和getTypes集合
addSetMethods(clazz); // 获取clazz中的setter方法,并填充setMethods集合和setTypes集合
addFields(clazz); // 处理没有getter/setter的字段
// 根据getMethods/setMethods集合,初始化可读/写属性的名称集合
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);
}
}
首先初始化 type
属性,然后在 addDefaultConstructor() 方法中根据传入的Class对象的默认构造方法初始化 defaultConstructor
属性,接着调用 addGetMethods() 方法填充 getMethods
和 getTypes
集合,同理调用 addSetMethods() 方法填充 setMethods
和 setTypes
集合,调用 addFields() 方法去处理没有getter/setter的其余字段,因为底层本质是通过反射来获取和设置属性值,该方法也会填充上述的四个集合,最后再将收集到的属性名根据允许获取和设置值来填充 readablePropertyNames
和 writeablePropertyNames
集合,再将这两个集合中的所有属性名的大写作为key来填充 caseInsensitivePropertyMap
。
四、方法功能
1、addDefaultConstructor(Class<?> clazz)
【功能】获取类的默认构造方法。
【源码与注释】
private void addDefaultConstructor(Class<?> clazz) {
// 获取类的所有构造方法
Constructor<?>[] consts = clazz.getDeclaredConstructors();
// 遍历所有构造方法
for (Constructor<?> constructor : consts) {
// 找到无参构造方法,该方法即为默认构造方法
if (constructor.getParameterTypes().length == 0) {
// 构造方法有可能被private修饰,需要设置其可以访问
if (canAccessPrivateMethods()) {
try {
constructor.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
// 如果无参构造方法有访问的权限,则赋值给defaultConstructor
if (constructor.isAccessible()) {
this.defaultConstructor = constructor;
}
}
}
}
2、canAccessPrivateMethods()
【功能】判断系统安全管理器是否允许修改类的可访问性。
【源码与注释】
// 判断是否可以修改可访问性
private static boolean canAccessPrivateMethods() {
try {
// 获取系统安全管理器
SecurityManager securityManager = System.getSecurityManager();
// 如果设置了安全管理器,检查是否有通过反射来访问protected、private的成员和方法的权限
if (null != securityManager) {
securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
}
} catch (SecurityException e) {
// 如果没有权限,则权限检查会抛出异常,返回false表示不允许访问私有方法
return false;
}
// 如果未设置安全管理器,或者有指定权限,则返回true表示允许访问私有方法
return true;
}
3、addGetMethods()
【功能】获取类的 getter 方法并填充 getMethods
和 getTypes
集合。
【源码与注释】
private void addGetMethods(Class<?> cls) {
// (1)定义属性名和对应getter方法的映射
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
// (2)获取当前类及其父类中定义的所有方法
Method[] methods = getClassMethods(cls);
// (3)遍历所有方法,筛选出 getter 方法并添加到 conflictingGetters 中
for (Method method : methods) {
String name = method.getName();
// 筛选出getter方法: 一般属性名为aaBb对应的getter方法为getAaBb()
// 如果属性是布尔值,则对应的getter方法为isAaBb()
// 如果子类重写覆盖了父类getter方法导致方法签名不一致
// eg:
// 父类定义: List<String> getList() 方法签名为: java.util.List<String>#getList
// 子类定义: ArrayList<String> getList() 方法签名为: java.util.ArrayList<String>#getList
// 我们要取的肯定是返回参数类型更加具体的方法,但是方法签名不一致导致冲突,就先将属性名和对应的冲突方法缓存到conflictingGetters
// 再交给resolveGetterConflicts()方法进一步处理
// (3.1)以 get 和 is 开头,并且方法名长度分别长于 3 和 2 才表明这是个 getter 方法
if (name.startsWith("get") && name.length() > 3) {
// (3.2)只有方法参数个数为0的方法才是 getter 方法
if (method.getParameterTypes().length == 0) {
// (3.3)根据方法名获取属性名
name = PropertyNamer.methodToProperty(name);
// (3.4)属性名和方法的映射添加到 conflictingGetters 中
addMethodConflict(conflictingGetters, name, method);
}
} else if (name.startsWith("is") && name.length() > 2) {
if (method.getParameterTypes().length == 0) {
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingGetters, name, method);
}
}
}
// (4)解决签名不同但是方法重复的 getter 方法,一般属性的 getter 只需要有一个即可
resolveGetterConflicts(conflictingGetters);
}
【解析】
(1)定义属性名和 getter 方法的映射,因为子类可能重写父类方法,比如返回值类型更加具体,所以映射的值是一个列表。
(2)调用 #getClassMethods()
方法来获取类的所有方法,包括实现接口定义的方法、继承自父类的方法,并沿着继承链向上追溯,具体实现可阅读下面关于该方法的详解。
(3)遍历所有方法,筛选出 getter 方法
- (3.1)只有形如
getXxx()
和isYyy()
的方法才是 getter 方法 - (3.2)getter 方法的参数个数应该为0
- (3.3)根据方法名获得属性名
- (3.4)属性名和方法的映射添加到 conflictingGetters 中,如果属性名第一次添加方法,则创建List,否则添加到已经创建的List中,源码如下:
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
// 获取List
List<Method> list = conflictingMethods.get(name);
// List为空,说明该属性是第一次,先创建List,再将List添加到conflictingMethods中
if (list == null) {
list = new ArrayList<Method>();
conflictingMethods.put(name, list);
}
// 往Method List中添加Method
list.add(method);
}
(4)调用 #resolveGetterConflicts(Map<String, List<Method>>)
方法解决签名不同但是方法重复的 getter 方法的问题,一般属性的 getter 只需要有一个即可,详解参见下面对该方法的介绍。
4、getClassMethods(Class<?> cls)
【功能】获取类cls及其超类中声明的方法,及其实现的接口方法,使用本方法而不是更简单的 Class.getMethods()
是因为我们也想获取被声明为 private 的方法。
【源码与注释】
private Method[] getClassMethods(Class<?> cls) {
// (1)用于记录指定类中定义的全部方法的唯一签名以及对应的Method对象
Map<String, Method> uniqueMethods = new HashMap<String, Method>();
// (2)表示当前在while循环体中被解析声明方法所属的类,将从cls到cls的父类,最终为Object类
Class<?> currentClass = cls;
while (currentClass != null) {
// (3)记录currentClass这个类中定义的全部方法
// PS.只能获取类内定义的普通方法,对于继承的方法无法通过反射获取
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
// (4)我们也要寻找接口方法,因为类可能是抽象的,可能实现了接口方法,这些方法无法通过上面的反射获取
// 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());
}
// (5)获取父类,继续循环,获取从父类继承的方法
currentClass = currentClass.getSuperclass();
}
// (6)将 uniqueMethods 中的值即方法取出来放 methods 集合中
Collection<Method> methods = uniqueMethods.values();
// (7)将 methods 集合转化为 Method 数组返回
return methods.toArray(new Method[methods.size()]);
}
【解析】
(1)定义局部变量 uniqueMethods:存储方法签名和方法的映射关系,方法签名可以看成是每个方法的唯一标识,其组成规则可以看下面关于 #addUniqueMethods()
方法的详解。
(2)定义局部变量 currentClass:表示当前被解析方法的类,将一直沿着继承链向上循环,从cls到cls的父类,最终为Object类。
(3)调用 #addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods)
方法记录当前类中定义的所有方法到 uniqueMethods 中,其源码实现如下:
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
for (Method currentMethod : methods) {
// (3.1)过滤掉桥接方法,桥接方法不是类中定义的方法,而是编译器为了兼容自动生成的方法
if (!currentMethod.isBridge()) {
// (3.2)获取方法签名
// 通过Reflector.getSignature()方法得到的方法签名是: 返回值类型#方法名称:参数类型列表。
// 例如,Reflector.getSignature(Method)方法的唯一签名是:
// java.lang.String#getSignature:java.lang.reflect.Method
// 通过Reflector.getSignature()方法得到的方法签名是全局唯一的,可以作为该方法的唯一标识
String signature = getSignature(currentMethod);
// (3.3)检测在子类中是否已经添加过该方法,如果在子类中已经添加过,则表示子类覆盖了该方法,
// 无须再想uniqueMethods集合中添加该方法了
// 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)) {
if (canAccessPrivateMethods()) {
try {
currentMethod.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
// (3.4)记录该签名和方法的对应关系
uniqueMethods.put(signature, currentMethod);
}
}
}
}
- (3.1)首先过滤掉桥接方法,所谓桥接方法,就是编译器为了兼容JDK自动生成而不是代码中定义的方法
- (3.2)获取方法签名,源码如下:
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();
}
其组成规则为:方法返回值类型#方法名:参数1类型,参数2类型,参数3类型,...,参数名n类型
eg:void#addUniqueMethods:Map<String, Method>,Method[]
- (3.3)因为子类可能重写父类的方法,因此当前类为上一次循环类的父类时,有可能父类被子类重写的方法的签名跟子类是一样的,这种情况以子类中声明的方法为准,如果方法是私有的,则添加访问权限。
- (3.4)将方法签名和方法的映射添加到 uniqueMethods 中。
(4)获取当前类实现的接口,并遍历接口调用 #addUniqueMethods(Map<String, Method>, Method[])
获得接口中定义的方法。
(5)获取父类,继续循环,获取从父类继承的方法。
(6)循环结束后,将 uniqueMethods 中的值即获取到的类的所有方法放到 methods 集合中。
(7)将 methods 集合转化为 Method 数组返回。
5、resolveGetterConflicts(Map<String, List<Method>> conflictingGetters)
【功能】解决一个属性有多个 getter 方法的问题,并调用 #addGetMethod(String name, Method method)
方法真正填充 getMethods
和 getTypes
集合。
【源码与注释】
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
// (1)对每个属性的方法逐个处理
for (String propName : conflictingGetters.keySet()) {
List<Method> getters = conflictingGetters.get(propName);
Iterator<Method> iterator = getters.iterator();
Method firstMethod = iterator.next();
// (2)该字段只有一个getter方法,没有冲突,直接添加到getMethods集合并填充getTypes集合
if (getters.size() == 1) {
addGetMethod(propName, firstMethod);
} else {
// (3)同一属性名称存在多个getter方法,则需要对比这些getter方法的返回值,选择getter方法
// 迭代过程中的临时变量,用于记录迭代到目前为止,最适合作为getter方法的Method
Method getter = firstMethod;
// (3.1)记录返回值类型
Class<?> getterType = firstMethod.getReturnType();
while (iterator.hasNext()) {
Method method = iterator.next();
Class<?> methodType = method.getReturnType();
if (methodType.equals(getterType)) {
// (3.2)返回值相同,实际上没有冲突,证明方法签名生成那个可能有问题了,抛出异常
throw new ReflectionException("Illegal overloaded getter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass()
+ ". This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
} else if (methodType.isAssignableFrom(getterType)) {
// (3.3)假如判断结果为true,表示methodType是getterType的继承父类或实现的接口
// 当前最合适的方法(getter)的返回值是当前方法(method)返回值的子类,什么都不做,当前最适合的方法依然不变
// OK getter type is descendant
} else if (getterType.isAssignableFrom(methodType)) {
// (3.4)当前方法的返回值是当前最适合的方法的返回值的子类,更新临时变量getter,当前的getter方法成为最适合的getter方法
getter = method;
getterType = methodType;
} else {
// (3.5)返回值相同,二义性,抛出异常
throw new ReflectionException("Illegal overloaded getter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass()
+ ". This breaks the JavaBeans " + "specification and can cause unpredicatble results.");
}
}
// (4)while循环结束筛选出最合适的方法之后,调用addGetMethod()方法填充getMethods和getTypes集合
addGetMethod(propName, getter);
}
}
}
【解析】
(1)遍历逐个处理每个属性,因为子类可以覆盖重写父类的方法,所以一个属性可能有多个 getter 方法
(2)如果属性只有一个 getter 方法,没有冲突,则直接调用 #addGetMethod(String name, Method method)
方法填充处理。
(3)同一属性可能有多个 getter 方法,定义局部变量 getter 存储当前最合适的getter
- (3.1)定义局部变量 getterType,记录 getter 的返回类型。
- (3.2)因为 getter 方法名相同,且不带参数,所以如果子类和父类方法返回值类型完全相同,说明要么子类画蛇添足方法跟父类相同如下所示,要么证明生成方法签名的方法可能有问题,此时抛出异常。
// 父类
public class SupClass {
private List userList;
public List getUserList() {
return userList;
}
}
// 子类
public class SubClass {
private List userList;
public List getUserList() {
return userList;
}
}
- (3.3)如果遍历到的其他 getter 方法的返回值类型是当前最适合的 getter 方法返回值类型的父类,则
getter
依然是最合适的方法,举例如下:
// 父类
public class SupClass() {
private List userList;
// 当前遍历到的method,methodType为List
public List getUserList() {
return userList;
}
}
// 子类
public class SubClass {
private ArrayList userList;
// 当前遍历筛选到的最合适的getter,getterType为ArrayList
public ArrayList getUserList() {
return userList;
}
}
- (3.4)跟上面的情况反过来,只是此时
getter
为父类的 getUserList 方法,getterType
为 List,这时遍历到更合适的子类的方法,因为其返回类型更加具体,所以更新替换getter
和getterType
。 - (3.5)如果不同的 getter 只是刚好方法名相同,实际上返回类型既不完全相同,也没有继承派生关系,则证明开发人员写的类不符合 JavaBean 的规范,此时直接抛出异常。
- (4)找到最合适的 getter 方法之后,调用
#addGetMethod(String name, Method method)
方法填充处理。
6、addGetMethod(String name, Method method)
【功能】筛选合法的属性名,将方法封装成 MethodInvoker
作为 value,属性名作为 key 填充 getMethods
集合;解析方法返回的真正类型(因为有可能带泛型、通配符等)作为 value,属性名作为 key 填充 getTypes
集合。
【源码与注释】
private void addGetMethod(String name, Method method) {
// (1)筛选过滤掉非法的属性名
if (isValidPropertyName(name)) {
// (2)将属性名以及对应的MethodInvoker对象添加到getMethods集合中,MethodInvoker是对Method对象反射操作的封装
getMethods.put(name, new MethodInvoker(method));
// (3)因为方法返回值中可能带有泛型,eg: List<T>
// 所以通过TypeParameterResolver.resolveReturnType()方法将类型信息解析到Type对象中
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// (4)将属性名称和对应的返回值类型添加到getTypes集合中保存
getTypes.put(name, typeToClass(returnType));
}
}
【解析】
(1)调用 #isValidPropertyName(String)
方法过滤出合理的属性名,合理的标准是:
- 属性名不以
$
开头 + 属性名不为serialVersionUID
(类实现序列化接口要定义的属性)+ 属性名不为class
其源码如下:
private boolean isValidPropertyName(String name) {
return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
}
(2)根据 method 对象封装创建 MethodInvoker
作为 value,属性名为 key 填充 getMethods
集合。
(3)调用 #TypeParameterResolver.resolveReturnType(Method method, Type srcType)
解析返回值的具体类型(关于该工具类的解析过程可参考:[MyBatis源码分析 - 反射器模块 - 组件四] TypeParameterResolver
),并调用 #typeToClass(Type src)
方法将返回类型对应的 Type 对象转化为 Class 对象并填充 getTypes
集合。,其源码如下:
private Class<?> typeToClass(Type src) {
Class<?> result = null;
// 普通类型,直接使用类
if (src instanceof Class) {
result = (Class<?>) src;
// 参数化类型,使用原始类型,如参数化类型List<String>的原始类型是List
} else if (src instanceof ParameterizedType) {
result = (Class<?>) ((ParameterizedType) src).getRawType();
// 泛型数据类型,形如A<T>[]或T[],前者的数组元素类型是ParameterizedType,后者是TypeVariable
// 当然,经过TypeParameterResolver的处理,里面的泛型已被解析为具体类型,如List<String>[]、String[]
} else if (src instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) src).getGenericComponentType();
// 数组元素已经是一个具体类型,直接返回形如String[]对应的Class对象即可
if (componentType instanceof Class) {
result = Array.newInstance((Class<?>) componentType, 0).getClass();
} else {
// 数组元素依然不是一个普通类型,如List<String>[],递归调用typetoClass方法处理,如上会返回List
// 最终返回的result为List[]
Class<?> componentClass = typeToClass(componentType);
result = Array.newInstance((Class<?>) componentClass, 0).getClass();
}
}
if (result == null) {
result = Object.class;
}
return result;
}
7、addSetMethods(Class<?> cls)
【功能】获取类的 setter 方法,并提取属性名,填充 setMethods
和 setTypes
集合。
【源码与注解】
private void addSetMethods(Class<?> cls) {
// 属性名与其 setter 方法的映射
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
// 获取类的所有方法
Method[] methods = getClassMethods(cls);
// 遍历所有方法
for (Method method : methods) {
String name = method.getName();
// 筛选出符合 setter 方法名规则的方法
if (name.startsWith("set") && name.length() > 3) {
// setter 方法的参数个数应该为1
if (method.getParameterTypes().length == 1) {
// 根据方法名提取出属性名,eg: getUser() => user
name = PropertyNamer.methodToProperty(name);
// 将属性与其 setter 方法的映射添加到conflictingSetters中
addMethodConflict(conflictingSetters, name, method);
}
}
}
// 解决同个属性名有多个冲突的 setter 方法的问题
resolveSetterConflicts(conflictingSetters);
}
【解析】
跟 #addGetMethods(Class<?> cls)
方法的处理过程大同小异,setter 方法不管参数类型是否布尔值,方法名都形如 setXxx,并且参数个数必须为1,然后提取属性名,添加到 conflictingSetters
映射中,最后调用 #resolveSetterConflicts(Map<String, List<Method>> conflictingSetters)
方法处理 setter 方法冲突。
8、resolveSetterConflicts(Map<String, List<Method>> conflictingSetters)
【功能】为类的每个属性挑选出最合适的 setter 方法,并调用 #addSetMethod(String name, Method method)
方法处理填充 setMethods
和 setTypes
集合。
【源码与注解】
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
// (1)逐个处理每个属性,为其找到最合适的 setter 方法,因为子类可以覆盖父类的方法,所以一个属性可能有多个 setter 方法
for (String propName : conflictingSetters.keySet()) {
List<Method> setters = conflictingSetters.get(propName);
Method firstMethod = setters.get(0);
// (2)只有一个 setter 方法,直接调用addSetMethod方法填充setMethods和setTypes集合
if (setters.size() == 1) {
addSetMethod(propName, firstMethod);
} else {
// (3)获取正在筛选 setter 方法的属性的 getter 方法的返回类型,应该跟 setter 的参数类型一致
Class<?> expectedType = getTypes.get(propName);
// (3.1)属性有setter却没有getter方法,存在二义性,不符合JavaBeans的规范,直接抛出异常
if (expectedType == null) {
throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " +
"specification and can cause unpredicatble results.");
} else {
Iterator<Method> methods = setters.iterator();
Method setter = null;
while (methods.hasNext()) {
Method method = methods.next();
// (3.2)setter参数个数应该只有一个并且参数类型跟getter方法的返回值类型相同,不符合则循环
if (method.getParameterTypes().length == 1
&& expectedType.equals(method.getParameterTypes()[0])) {
setter = method;
break;
}
}
// (4)如果循环结束还没找到setter方法,则抛出异常
if (setter == null) {
throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " +
"specification and can cause unpredicatble results.");
}
// (5)while循环结束筛选出最合适的方法之后,调用addSetMethod()方法填充setMethods和setTypes集合
addSetMethod(propName, setter);
}
}
}
}
【详解】
(1)(2)步的处理与 #resolveGetterConflicts(Map<String, List<Method>> conflictingGetters)
类似。
(3)一个符合 JavaBean 规范的类应该同时有 getter 和 setter,并且 getter 的返回类型和 setter 的参数类型一致;程序先去处理 getter,所以这里先根据属性名从 getTypes
获取到 getter 的返回类型。
- (3.1)如果返回类型为空,证明该属性没有 getter 方法,这里抛出异常。
- (3.2)如果不为空,则遍历所有的 setter,找到参数个数为1并且参数类型与 getter 返回类型相同的方法,找到即赋值给变量
setter
,并跳出循环。
(4)如果没找到符合规范的 setter 方法,则抛出异常。
(5)调用#addSetMethod(String name, Method method)
方法真正填充setMethods
和setTypes
集合,其代码很简单,同样跟上面的#addGetMethod(String name, Method method)
类似,源码如下:
private void addSetMethod(String name, Method method) {
// 筛选过滤掉非法的属性名
if (isValidPropertyName(name)) {
// 将属性名以及对应的MethodInvoker对象添加到setMethods集合中,MethodInvoker是对Method对象反射操作的封装
setMethods.put(name, new MethodInvoker(method));
// 因为方法返回值中可能带有泛型,eg: List<T>
// 所以通过TypeParameterResolver.resolveReturnType()方法将类型信息解析到Type对象中
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
// 将属性名称和对应的返回值类型添加到setTypes集合中保存
setTypes.put(name, typeToClass(paramTypes[0]));
}
}
9、addFields(Class<?> clazz)
【功能】对于没有 getter 和 setter 的属性,通过本方法处理,填充到 getMethods
、getTypes
、setMethods
、setTypes
集合中,在获取设置这些属性值时,是通过 Field
对象的反射方法 get
、set
实现的,是对 #addSetMethods(...)
和 #addGetMethods(...)
方法的补充。
【源码与注解】
private void addFields(Class<?> clazz) {
// (1)获取类中声明的所有field属性,包含了已经解析出来的有 getter&setter 的属性
Field[] fields = clazz.getDeclaredFields();
// 遍历field属性
for (Field field : fields) {
// 设置私有属性访问权限
if (canAccessPrivateMethods()) {
try {
field.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
if (field.isAccessible()) {
// (2)过滤掉已经有 getter 和 setter 的属性
if (!setMethods.containsKey(field.getName())) {
// (2.1)获取类的修饰符,过滤掉被声明为static final的属性,这些属性只能被类加载器设置其初始值
int modifiers = field.getModifiers();
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
// (2.2)
addSetField(field);
}
}
if (!getMethods.containsKey(field.getName())) {
// (2.3)
addGetField(field);
}
}
}
// (3)获取父类,找出父类中声明的属性
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
【详解】
(1)获取类中声明的所有field属性,包含了已经解析出来的有 getter&setter 的属性。
(2)过滤掉已经有 getter&setter 的属性。
- (2.1)过滤掉被声明为
static final
的属性,这类属性只允许被类加载器初始化。 - (2.2)调用
#addSetField(Field field)
填充setMethods
和setTypes
集合,源码如下:
private void addSetField(Field field) {
// 过滤掉非法的属性
if (isValidPropertyName(field.getName())) {
// 创建封装了属性对应Field对象的SetFieldInvoker对象,内部利用Field的set方法来反射设置属性值
setMethods.put(field.getName(), new SetFieldInvoker(field));
// 解析获取属性值的具体类型(如果带泛型或通配符)
Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
// 将属性和属性值类型对应的Class对象映射填充setTypes集合
setTypes.put(field.getName(), typeToClass(fieldType));
}
}
- (2.3)调用
#addGetField(Field field)
填充getMethods
和getTypes
集合,源码如下:
private void addGetField(Field field) {
// 过滤掉非法的属性
if (isValidPropertyName(field.getName())) {
// 创建封装了属性对应Field对象的GetFieldInvoker对象,内部利用Field的get方法来反射设置属性值
getMethods.put(field.getName(), new GetFieldInvoker(field));
// 解析获取属性值的具体类型(如果带泛型或通配符)
Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
// 将属性和属性值类型对应的Class对象映射填充setTypes集合
getTypes.put(field.getName(), typeToClass(fieldType));
}
}
(3)获取父类,通过递归调用本方法找出父类中声明的属性。
10、其他方法
【功能】主要是对 Reflector
类中的属性的使用,对外暴露使用的方法。
- (1)
#hasDefaultConstructor()
:判断被解析的类是否有构造方法。 - (2)
#getSetInvoker(String propertyName)
:根据属性名获取对应的 Invoker 对象,可通过该对象设置属性值。 - (3)
#getGetInvoker(String propertyName)
:根据属性名获取对应的 Invoker 对象,可通过该对象获取属性值。 - (4)
#getSetterType(String propertyName)
:根据属性名获取对应的 setter 方法的参数类型对应的 Class 对象。 - (5)
#getGetterType(String propertyName)
:根据属性名获取对应的 getter 方法的返回值类型对应的 Class 对象。 - (6)
#getGetablePropertyNames()
:获取类的所有可读属性。 - (7)
#getSetablePropertyNames()
:获取类的所有可写属性。 - (8)
#hasSetter(String propertyName)
:判断传入的属性名是否有 setter,可用来判断被解析的类是否有某属性或是否可设置某属性。 - (9)
#hasGetter(String propertyName)
:判断传入的属性名是否有 getter,可用来判断被解析的类是否有某属性或是否可读取某属性。 - (10)
#findPropertyName(String name)
:根据传入不区分大小写的属性名获取实际真实的属性名。
五、ReflectorFactory
ReflectorFactory
是一个接口,定义了创建和缓存 Reflector 对象的方法,只有一个实现类 DefaultReflectorFactory
,类关系图如下:
ReflectorFactory
接口定义如下:
public interface ReflectorFactory {
// 判断和设置是否有缓存Reflector对象
boolean isClassCacheEnabled();
void setClassCacheEnabled(boolean classCacheEnabled);
// 根据传入的Class对象,创建对应的Reflector对象
Reflector findForClass(Class<?> type);
}
【解析】
#isClassCacheEnabled()
、#setClassCacheEnabled()
方法提供了调用代码Reflector工厂对象是否有缓存生成的 Reflector 对象的信息,#findForClass(Class<?> type)
方法则是根据传入的 Class 对象创建 Reflector 对象。
唯一一个实现类 DefaultReflectorFactory
的源码如下:
public class DefaultReflectorFactory implements ReflectorFactory {
// 判断是否要缓存Reflector对象的标志位
private boolean classCacheEnabled = true;
// Reflector对象缓存
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();
public DefaultReflectorFactory() {
}
@Override
public boolean isClassCacheEnabled() {
return classCacheEnabled;
}
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
@Override
public Reflector findForClass(Class<?> type) {
// 如果有缓存,则先尝试从缓存中取,如果缓存中没有则先创建Reflector对象,再放入缓存,最后返回
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
// 如果没有缓存,直接创建Reflector对象即可
return new Reflector(type);
}
}
}
【解析】
代码比较简单,通过一个布尔型的标志位 classCacheEnabled
判断是否要对生成的 Reflector 对象进行缓存,前两个接口方法的实现也依赖于这个标志位;定义了一个并发容器 reflectorMap
缓存 Class 对象及 Reflector 对象的映射关系,将并发控制委托给 ConcurrentMap,#findForClass()
方法中,如果有缓存,先从缓存中取,如果不为空直接返回,如果为空则先创建再缓存最后返回,如果没缓存,则每次都直接创建返回。
六、测试案例
先来个简单的,不带继承关系,不带泛型的JavaBean,如下:
public class ReflectorTest {
// 简单Java类
static class SimpleJavaBean {
private Long id;
private String username;
private double money;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "{ id:" + id + ", username:" + username + ", money:" + money + " }";
}
}
}
测试案例覆盖了 Reflector
对外暴露的所有方法,单元测试代码如下:
@Test
public void testSimpleJavaBean() throws InvocationTargetException, IllegalAccessException {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(SimpleJavaBean.class);
Assert.assertTrue(reflector.hasDefaultConstructor());
String[] readablePropertyNames = reflector.getGetablePropertyNames();
Assert.assertEquals(3, readablePropertyNames.length);
// 输出 money id username,虽然money没有显式的getter,但是可以通过反射获取,所以也是可读属性
System.out.println("[readablePropertyNames] ");
for (String name : readablePropertyNames)
System.out.print(name + " ");
System.out.println();
String[] writeablePropertyNames = reflector.getSetablePropertyNames();
Assert.assertEquals(3, writeablePropertyNames.length);
System.out.println("[writeablePropertyNames]");
for (String name : writeablePropertyNames)
System.out.print(name + " ");
System.out.println();
Assert.assertEquals(Long.class, reflector.getGetterType("id"));
Assert.assertEquals(String.class, reflector.getSetterType("username"));
Assert.assertEquals(double.class, reflector.getGetterType("money"));
Assert.assertTrue(reflector.hasGetter("id"));
Assert.assertTrue(reflector.hasSetter("money"));
Assert.assertEquals("id", reflector.findPropertyName("ID"));
Assert.assertEquals("username", reflector.findPropertyName("Username"));
SimpleJavaBean bean = new SimpleJavaBean();
System.out.println("bean(before call Invoker): " + bean);
Invoker idSetInvoker = reflector.getSetInvoker("id");
idSetInvoker.invoke(bean, new Object[]{100L});
Invoker idGetInvoker = reflector.getGetInvoker("id");
Assert.assertEquals(100L, idGetInvoker.invoke(bean, new Object[] {}));
Invoker usernameSetInvoker = reflector.getSetInvoker("username");
usernameSetInvoker.invoke(bean, new Object[]{"zhangsan"});
Invoker moneySetInvoker = reflector.getSetInvoker("money");
moneySetInvoker.invoke(bean, new Object[]{2.33});
System.out.println("bean(after call Invoker): " + bean);
}
执行结果如下:
复杂一点,经过上面的源码分析可知,解析类属性时不仅会解析类中定义的,还会通过解析接口和父类获取 getter/setter 方法,最后解析出属性名和属性类型,所以定义一个测试接口和实现了接口的抽象父类,最后被测试的类继承了抽象父类,代码如下:
// 接口定义
static interface Entity<T> {
T getId();
void setId(T id);
}
// 抽象父类实现了接口
static abstract class AbstractEntity implements Entity<Long> {
private Long id;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
}
// 类继承了父类,间接实现了接口
static class Section extends AbstractEntity {
}
测试代码聚焦是否能获取到继承的属性和正确解析类型,如下:
@Test
public void testGetSetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Section.class);
Assert.assertEquals(Long.class, reflector.getSetterType("id"));
}
@Test
public void testGetGetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Section.class);
Assert.assertEquals(Long.class, reflector.getGetterType("id"));
}
@Test
public void shouldNotGetClass() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Section.class);
Assert.assertFalse(reflector.hasGetter("class"));
}
执行结果:
更复杂一些,属性的类型可能是比较复杂的类型,比如链表List、数组,属性可能是私有的,一般带泛型的类是设计用来复用的,其中子类继承带泛型的父类并且指定类型是一种常用的形式,所以类定义如下:
static abstract class Parent<T extends Serializable> {
protected T id;
protected List<T> list;
protected T[] array;
private T fld;
public T pubFld;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public T[] getArray() {
return array;
}
public void setArray(T[] array) {
this.array = array;
}
public T getFld() {
return fld;
}
}
static class Child extends Parent<String> {
}
测试代码如下:
// 测试是否能解析出泛型
@Test
public void shouldResolveSetterParam() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
assertEquals(String.class, reflector.getSetterType("id"));
}
// 测试解析复杂的类型链表
@Test
public void shouldResolveParameterizedSetterParam() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
assertEquals(List.class, reflector.getSetterType("list"));
}
// 测试解析复杂的类型数组
@Test
public void shouldResolveArraySetterParam() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
Class<?> clazz = reflector.getSetterType("array");
assertTrue(clazz.isArray());
assertEquals(String.class, clazz.getComponentType());
}
@Test
public void shouldResolveGetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
assertEquals(String.class, reflector.getGetterType("id"));
}
@Test
public void shouldResolveSetterTypeFromPrivateField() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
assertEquals(String.class, reflector.getSetterType("fld"));
}
@Test
public void shouldResolveGetterTypeFromPublicField() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
assertEquals(String.class, reflector.getGetterType("pubFld"));
}
@Test
public void shouldResolveParameterizedGetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
assertEquals(List.class, reflector.getGetterType("list"));
}
@Test
public void shouldResolveArrayGetterType() throws Exception {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(Child.class);
Class<?> clazz = reflector.getGetterType("array");
assertTrue(clazz.isArray());
assertEquals(String.class, clazz.getComponentType());
}
执行结果:
通过上面几个测试案例,可以比较好地学习掌握
Reflector
的用法。
网友评论