Mybatis源码学习之反射工具(三)

时间:2023-03-09 09:31:14
Mybatis源码学习之反射工具(三)

简述

MyBatis在进行参数处理、结果映射等操作时,会涉及大量的反射操作。Java中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射模块,该模块位于org.apache.ibatis.reflection包中,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射API。

Reflector & ReflectorFactory

Reflector

Reflector是MyBatis中反射模块的基础,每个Reflector对象都对应一个类,在Reflector中缓存了反射操作需要使用的类的元信息。Reflector 中各个字段的含义如下:


/**
* 缓存了反射操作需要使用的类的元信息。
* 允许在属性名和getter/setter方法之间轻松映射
*
* @author Clinton Begin
*/
public class Reflector { /**
* 对应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对象
* <p>
*/
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>(); /**
* 构造函数,对上述字段初始化
*/
public Reflector(Class<?> clazz) {
type = clazz;
addDefaultConstructor(clazz);
addGetMethods(clazz);
addSetMethods(clazz);
addFields(clazz);
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
} /**
* 获取指定Class对象的默认构造方法(包括无参构造方法)
*/
private void addDefaultConstructor(Class<?> clazz) {
Constructor<?>[] consts = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : consts) {
//默认构造方法没有参数
if (constructor.getParameterTypes().length == 0) {
//允许利用反射检查任意类的私有变量
if (canAccessPrivateMethods()) {
try {
constructor.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
if (constructor.isAccessible()) {
this.defaultConstructor = constructor;
}
}
}
} /**
* 负责解析类中定义的getter方法
*/
private void addGetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
if (method.getParameterTypes().length > 0) {
continue;
}
String name = method.getName();
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
//获取字段名
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingGetters, name, method);
}
}
resolveGetterConflicts(conflictingGetters);
} private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
//遍历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);
}
} /**
* 收集get方法
*/
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));
}
} /**
* 收集set方法
*/
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingSetters, name, method);
}
}
}
resolveSetterConflicts(conflictingSetters);
} private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
List<Method> list = conflictingMethods.get(name);
if (list == null) {
list = new ArrayList<Method>();
conflictingMethods.put(name, list);
}
list.add(method);
} private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
for (String propName : conflictingSetters.keySet()) {
List<Method> setters = conflictingSetters.get(propName);
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 {
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 {
addSetMethod(propName, match);
}
}
} 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() + "'.");
} private void addSetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
setMethods.put(name, new MethodInvoker(method));
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
setTypes.put(name, typeToClass(paramTypes[0]));
}
} private Class<?> typeToClass(Type src) {
Class<?> result = null;
if (src instanceof Class) {
result = (Class<?>) src;
} else if (src instanceof ParameterizedType) {
result = (Class<?>) ((ParameterizedType) src).getRawType();
} else if (src instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) src).getGenericComponentType();
if (componentType instanceof Class) {
result = Array.newInstance((Class<?>) componentType, 0).getClass();
} else {
Class<?> componentClass = typeToClass(componentType);
result = Array.newInstance((Class<?>) componentClass, 0).getClass();
}
}
if (result == null) {
result = Object.class;
}
return result;
} private void addFields(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
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()) {
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());
}
} private void addSetField(Field field) {
if (isValidPropertyName(field.getName())) {
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())) {
getMethods.put(field.getName(), new GetFieldInvoker(field));
Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
getTypes.put(field.getName(), typeToClass(fieldType));
}
} private boolean isValidPropertyName(String name) {
return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
} /*
* 此方法返回一个数组,该数组包含该类中声明的所有方法和任何超类
* 我们使用此方法不是为了代替 Class.getMethods(),
* 因为我们想访问类中的私有方法.
*
* @param cls Class对象
* @return 包含该类中所有方法的数组
*/
private Method[] getClassMethods(Class<?> cls) {
//用于记录指定类中定义的全部方法的唯一签名以及对应的Method对象
Map<String, Method> uniqueMethods = new HashMap<String, Method>();
Class<?> currentClass = cls;
while (currentClass != null) {
//记录当前类中定义的所有方法
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); // 记录接口中定义的方法
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()]);
} /**
* 为每个方法生成唯一签名,并记录到uniqueMethods集合中
*/
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
for (Method currentMethod : methods) {
if (!currentMethod.isBridge()) {
//得到方法签名
String signature = getSignature(currentMethod);
//根据方法签名排重
if (!uniqueMethods.containsKey(signature)) {
if (canAccessPrivateMethods()) {
try {
currentMethod.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
//记录签名与方法的对应关系
uniqueMethods.put(signature, currentMethod);
}
}
}
} /**
* 获取方法签名,eg:java.lang.String#getSignature:java.lang.reflect.Method
*/
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();
} /**
* 获取访问私有方法的权限
*/
private static boolean canAccessPrivateMethods() {
try {
SecurityManager securityManager = System.getSecurityManager();
if (null != securityManager) {
//允许利用反射检查任意类的私有变量
securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
}
} catch (SecurityException e) {
return false;
}
return true;
} /*
* Gets the name of the class the instance provides information for
*
* @return The class name
*/
public Class<?> getType() {
return type;
} public Constructor<?> getDefaultConstructor() {
if (defaultConstructor != null) {
return defaultConstructor;
} else {
throw new ReflectionException("There is no default constructor for " + type);
}
} public boolean hasDefaultConstructor() {
return defaultConstructor != null;
} public Invoker getSetInvoker(String propertyName) {
Invoker method = setMethods.get(propertyName);
if (method == null) {
throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
}
return method;
} public Invoker getGetInvoker(String propertyName) {
Invoker method = getMethods.get(propertyName);
if (method == null) {
throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
}
return method;
} /*
* Gets the type for a property setter
*
* @param propertyName - the name of the property
* @return The Class of the propery setter
*/
public Class<?> getSetterType(String propertyName) {
Class<?> clazz = setTypes.get(propertyName);
if (clazz == null) {
throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
}
return clazz;
} /*
* Gets the type for a property getter
*
* @param propertyName - the name of the property
* @return The Class of the propery getter
*/
public Class<?> getGetterType(String propertyName) {
Class<?> clazz = getTypes.get(propertyName);
if (clazz == null) {
throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
}
return clazz;
} /*
* Gets an array of the readable properties for an object
*
* @return The array
*/
public String[] getGetablePropertyNames() {
return readablePropertyNames;
} /*
* Gets an array of the writeable properties for an object
*
* @return The array
*/
public String[] getSetablePropertyNames() {
return writeablePropertyNames;
} /*
* Check to see if a class has a writeable property by name
*
* @param propertyName - the name of the property to check
* @return True if the object has a writeable property by the name
*/
public boolean hasSetter(String propertyName) {
return setMethods.keySet().contains(propertyName);
} /*
* Check to see if a class has a readable property by name
*
* @param propertyName - the name of the property to check
* @return True if the object has a readable property by the name
*/
public boolean hasGetter(String propertyName) {
return getMethods.keySet().contains(propertyName);
} public String findPropertyName(String name) {
return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
}
}

Reflector.addGetMethods()方法主要负责解析类中定义的getter方法,Reflector.addSetMethods()方法负责解析类中定义的setter方法,两者的逻辑类似,具体实现见源码。

ReflectorFactory接口

ReflectorFactory接口主要实现了对Reflector对象的创建和缓存,该接口定义如下:

public interface ReflectorFactory {

    /**
* 是否会缓存Reflector对象
*/
boolean isClassCacheEnabled(); /**
* 设置是否缓存Reflector对象
*/
void setClassCacheEnabled(boolean classCacheEnabled); /**
* 创建指定Class的Reflector对象
*/
Reflector findForClass(Class<?> type);
}

MyBatis只为该接口提供了DefaultReflectorFactory这一个实现类,它与Reflector的关系如下:

Mybatis源码学习之反射工具(三)

DefaultReflectorFactory各字段及方法的含义如下:

public class DefaultReflectorFactory implements ReflectorFactory {
/**
* 默认开启对Reflector对象的缓存
*/
private boolean classCacheEnabled = true; /**
* 使用集合ConcurrentHashMap实现对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;
} /**
* 为指定的Class创建Reflector对象,并将Reflector对象缓存到reflectorMap中
*/
@Override
public Reflector findForClass(Class<?> type) {
//检查是否开启缓存
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type);
if (cached == null) {
//创建Reflector对象
cached = new Reflector(type);
//放入ConcurrentHashMap集合缓存
reflectorMap.put(type, cached);
}
return cached;
} else {
//没有开启缓存,直接创建Reflector对象并返回
return new Reflector(type);
}
} }

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

TypeParameterResolver

TypeParameterResolver是一个工具类,提供了一系列静态方法来解析指定类中的字段、方法返回值或方法参数的类型。

在开始介绍TypeParameterResolver之前,先简单介绍一下Type接口的基础知识。Type是所有类型的父接口,它有四个子接口和一个实现类,

Mybatis源码学习之反射工具(三)

1、Class比较常见,它表示的是原始类型。

Class类的对象表示JVM中的一个类或接口,每个Java类在JVM里都表现为一个Class对象。在程序中可以通过“类名.class”、“对象.getClass()”或是“Class.forName(”类名”)”等方式获取Class对象。数组也被映射为Class 对象,所有元素类型相同且维数相同的数组都共享同一个 Class 对象。

2、ParameterizedType表示的是参数化类型

例如List<String>、Map<Integer,String>、Service<User>这种带有泛型的类型。

ParameterizedType接口中常用的方法有三个,分别是:

Type getRawType()—返回参数化类型中的原始类型,例如List<String>的原始类型为List。 

Type[] getActualTypeArguments()—获取参数化类型的类型变量或是实际类型列表,例如Map<Integer, String>的实际泛型列表Integer和String。需要注意的是,该列表的元素类型都是Type,也就是说,可能存在多层嵌套的情况。 

Type getOwnerType()—返回是类型所属的类型,例如存在A<T>类,其中定义了内部类InnerA<I>,则InnerA<I>所属的类型为A<T>,如果是顶层类型则返回null。这种关系比较常见的示例是Map<K,V>接口与Map.Entry<K,V>接口,Map<K,V>接口是Map.Entry<K,V>接口的所有者。 · TypeVariable表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List<T>中的T就是类型变量,它在编译时需被转换为一个具体的类型后才能正常使用。

TypeParameterResolver中各个静态方法之间的调用关系大致如图所示

Mybatis源码学习之反射工具(三)

TypeParameterResolver中通过resolveFieldType()方法、resolveReturnType()方法、resolveParamTypes()方法分别解析字段类型、方法返回值类型和方法参数列表中各个参数的类型。具体实现可查看其源码及官方的单元测试方法。

ObjectFactory接口

MyBatis中有很多模块会使用到ObjectFactory接口,该接口提供了多个create()方法的重载,通过这些create()方法可以创建指定类型的对象。ObjectFactory接口的定义如下:

/**
* Mybatis 使用ObjectFactory去创建需要的对象
* @author Clinton Begin
*/
public interface ObjectFactory { /**
* 设置配置信息
* @param properties 配置信息
*/
void setProperties(Properties properties); /**
* 通过默认构造函数创建指定类的对象
* @param type 对象类型
* @return
*/
<T> T create(Class<T> type); /**
* 根据参数列表,从指定类型中选择合适的构造器创建对象
* @param type 对象类型
* @param constructorArgTypes 参数类型
* @param constructorArgs 参数值
* @return
*/
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs); /**
* 检测指定类型是否是集合类型
* @param type 对象类型
* @return 集合类型返回true否则返回false
* @since 3.1.0
*/
<T> boolean isCollection(Class<T> type); }

DefaultObjectFactory是MyBatis提供的ObjectFactory接口的唯一实现,它是一个反射工厂,其create()方法通过调用instantiateClass()方法实现。DefaultObjectFactory.instantiateClass()方法会根据传入传入的参数列表选择合适的构造函数实例化对象,具体实现如下:

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
//通过无参构造函数创建对象
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
//根据参数列表查找合适的构造函数,并实例化对象
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
for (Class<?> argType : constructorArgTypes) {
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
for (Object argValue : constructorArgs) {
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
}
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}

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