Reflector和ReflectorFactory
ReflectorFactory
/**
* 工厂接口的默认实现
*/
public class DefaultReflectorFactory implements ReflectorFactory {
//该字段决定了是否对开启对Reflector的缓存
private boolean classCacheEnabled = true;
//使用线程安全的map对Reflector对象进行缓存
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;
}
/**
* 生产Reflector对象
* @param type 目标类型
* @return 目标类型的Reflector对象
*/
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) { // 允许缓存
// 生产入参type的反射器对象,并放入缓存
// java8之后。上面的操作可以简化为一行,若key对应的value为空,会将第二个参数的返回值存入并返回
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
}
很简单,主要是用一个线程安全的ConcurrentHashMap对Relectoru进行缓存。
Reflector
public class Reflector {
// 要被反射解析的类
private final Class<?> type;
// 能够读的属性列表,即有get方法的属性列表
private final String[] readablePropertyNames;
// 能够写的属性列表,即有set方法的属性列表
private final String[] writablePropertyNames;
// set方法映射表。键为属性名,值为对应的set方法
private final Map<String, Invoker> setMethods = new HashMap<>();
// get方法映射表。键为属性名,值为对应的get方法
private final Map<String, Invoker> getMethods = new HashMap<>();
// set方法输入类型。键为属性名,值为对应的该属性的set方法的类型(实际为set方法的第一个参数的类型)
private final Map<String, Class<?>> setTypes = new HashMap<>();
// get方法输出类型。键为属性名,值为对应的该属性的get方法的类型(实际为get方法的返回值类型)
private final Map<String, Class<?>> getTypes = new HashMap<>();
// 默认构造函数
private Constructor<?> defaultConstructor;
// 大小写无关的属性映射表。键为属性名全大写值,值为属性名
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
这个类的主要目的是通过一个类的Class类,将这个类的各种方法,与根据javaBean约定获取get/set方法(获取属性名然后get/set拼接),然后将解析的出来的各个方法名称等(包括实现的接口,继承的父类)。这个类的初始化就是通过一个目标Class类进行分析,将其所有的内容分别存储到Reflector中。
/**
* Reflector的构造方法
* @param clazz 需要被反射处理的目标类
*/
public Reflector(Class<?> clazz) {
// 要被反射解析的类
type = clazz;
// 设置默认构造器属性
addDefaultConstructor(clazz);
// 处理clazz中的getter方法,填充getMethods集合和getTypes集合
addGetMethods(clazz);
// 解析所有有setter
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);
}
}
addDefaultConstructor(clazz)
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
// 无参构造函数即为默认构造函数
Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
.findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
}
- constructor.getParameterTypes() – 获取构造方法的参数类型数组
addGetMethods(clazz) – 存储所有的get方法
/**
* 找出类中的get方法
* @param clazz 需要被反射处理的目标类
*/
private void addGetMethods(Class<?> clazz) {
// 存储属性的get方法。Map的键为属性名,值为get方法列表。某个属性的get方法用列表存储是因为前期可能会为某一个属性找到多个可能get方法。
Map<String, List<Method>> conflictingGetters = new HashMap<>();
// 找出该类中所有的方法
Method[] methods = getClassMethods(clazz);
// 过滤出get方法,过滤条件有:无入参、符合Java Bean的命名规则;然后取出方法对应的属性名、方法,放入conflictingGetters
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
// 如果一个属性有多个疑似get方法,resolveGetterConflicts用来找出合适的那个
resolveGetterConflicts(conflictingGetters);
}
- Method[] methods = getClassMethods(clazz); – 获取所有的get方法,包括私有,父类和实现的接口get方法等。
private Method[] getClassMethods(Class<?> clazz) {
//用于记录指定类中定义的全部方法的唯一签名以及对应的 Method 对象
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
while (currentClass != null && currentClass != Object.class) {
//记录 currentClass 这个类中定义的全部方法
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[0]);
}
- 这段代码分为了三部分:在类不为空和不是Object类的情况下
- 从当前类获取所有的方法
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
- 从实现的接口的获取所有的方法
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
- 从继承的父类中获取所有的方法
currentClass = currentClass.getSuperclass();
我们一个一个来看:
addUniqueMethods 从当前类获取所有的方法
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
for (Method currentMethod : methods) {
if (!currentMethod.isBridge()) {
//通过 Reflector.getSignature () 方法得到的方法签名是:返回值类型#方法名称:参
//数类型列表 例如, Reflector.getSignature(Method )方法的唯一签名是:
//java.lang.String#getSignature:]java.lang.reflect.Method
//通过 Reflector.getSignature()方法得到的方法签名是全局唯一的,可以作为该方法的唯一标识
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);
}
}
}
}
- currentMethod.isBridge()桥接方法,是为了兼容用的。
- String signature = getSignature(currentMethod); – mybatis自己设计的一个方法,其目的是为了给方法一个唯一签名,并记录到uniqueMethods中。签名格式:返回值类型#方法名称:参数类型列表
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++) {
sb.append(i == 0 ? ':' : ',').append(parameters[i].getName());
}
return sb.toString();
}
从其实现的接口中获取方法
- Class<?>[] interfaces = currentClass.getInterfaces();很简单,就不多说了
从父类中获取实现的方法
- currentClass = currentClass.getSuperclass();获取到父类后,继续走while循环,也很简单。
至此,获取该类所有的方法已经结束,需要对其进行过滤已找出符合要求的get方法
// 过滤出get方法,过滤条件有:无入参、符合Java Bean的命名规则;然后取出方法对应的属性名、方法,放入conflictingGetters
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
过滤的原则:
- m.getParameterTypes().length == 0 方法的参数类型集合的长度为0
- PropertyNamer.isGetter(m.getName()) 判断方法的名称是否符合要求
- addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m)
// 将方法名转化为属性名
public static String methodToProperty(String name) {
if (name.startsWith("is")) {
name = name.substring(2);
} else if (name.startsWith("get") || name.startsWith("set")) {
name = name.substring(3);
} else {
throw new ReflectionException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'.");
}
// 将方法名中属性的大小写修改正确
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;
}
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
// 如果没有则创建列表,有则取出列表
// computeIfAbsent(name, k -> new ArrayList<>()) 方法为Map中的方法:
// 如果map中通过name索引到value,则返回value
// 否则将name作为输入交给lambda函数,直接lambda(name),则将name:lambda(name)写入map,并返回lambda(name)
List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
// 增加一个方法进入列表
list.add(method);
}
这段代码的是将参数名和方法集合(有可能有多个get方法)进行映射的真正代码
- conflictingMethods.computeIfAbsent(key,k -> value);
- 将key放入map中,如果key对应的之不存在则返回null,如果存在则返回value。
此处将computeIfAbsent的value返回(List),将方法添加到List集合中去。
下面开始解决存在多个get方法的冲突问题:resolveSetterConflicts(conflictingSetters);
// 如果一个属性有多个getter方法,该方法负责找出真正的
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
String propName = entry.getKey();
// 对key的多个getter进行循环
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)) {
// 不是布尔型,两个返回一样,则无法辨明到底哪个是getter
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")) {
// 是is的当选
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// candidateType返回了子类,则winner作为最终结果
} else if (winnerType.isAssignableFrom(candidateType)) {
// winner返回了子类,则candidate作为最终结果
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);
}
}
- 先判断两个get方法的返回值类型是否相同,不相同在比较是否是布尔值
- 在判断两个方法返回值类型是否为对方的爸爸,如果是爸爸就不管了:candidateType.isAssignableFrom(winnerType) 或者winnerType.isAssignableFrom(candidateType)
- boolean b = A.isAssignableFrom(B) 判断A是否是B的爸爸
addGetMethod
private void addGetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
getMethods.put(name, new MethodInvoker(method));
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 这里调用了
getTypes.put(name, typeToClass(returnType));
}
}
private boolean isValidPropertyName(String name) {
return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
}
- getMethods.put(name, new MethodInvoker(method));
public class MethodInvoker implements Invoker {
// 传入参数或者传出参数类型
private final Class<?> type;
private final Method method;
/**
* MethodInvoker构造方法
* @param method 方法
*/
public MethodInvoker(Method method) {
this.method = method;
if (method.getParameterTypes().length == 1) {
// 有且只有一个入参时,这里放入入参
type = method.getParameterTypes()[0];
} else {
type = method.getReturnType();
}
}
// 执行函数
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
method.setAccessible(true);
return method.invoke(target, args);
} else {
throw e;
}
}
}
@Override
public Class<?> getType() {
return type;
}
}
这个类的比较简单,就是存储了方法和方法的入参或者出参参数类型。
当方法的参数只有一个时,存储的是入参参数类型,反之,存储返回的参数类型。
- invoke()方法就是简单的反射执行当前存储的方法
将参数名称和方法存入对应的getMethods中,继续执行下一步:
- Type returnType = TypeParameterResolver.resolveReturnType(method, type);
- method是当前类的方法,type是当前的Class类
要了解这个TypeParamterResolver,需要前置的一点java的Type类的知识,可以参考
Type类相关知识。
/**
* @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
* they will be resolved to the actual runtime {@link Type}s.
*/
public static Type resolveReturnType(Method method, Type srcType) {
Type returnType = method.getGenericReturnType();
Class<?> declaringClass = method.getDeclaringClass();
return resolveType(returnType, srcType, declaringClass);
}