MyBatis源码解析 - 反射模块
MyBatis源码解析 - 反射模块
1. 前言
该模块位于org.apache.ibatis.reflection
包中,MyBatis在进行参数处理、结果映射等操作时,会涉及大量的反射操作。Java 中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射模块,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射API。本节就来为读者介绍该模块中核心代码的实现。
2. Reflector
Reflector 是MyBaits中反射模块的基础,Reflector中缓存了很多反射操作需要使用的元数据。各个字段如下:
private final Class<?> type; //对应的class类型
private final String[] readablePropertyNames; //可读属性的名称集合,可读属性就是存在相应getter方法的属性
private final String[] writablePropertyNames; //可写属性的名称集合,可写属性就是存在相应setter方法的属性
//记录了属性相应的setter方法,key是属性名称,value 是Invoker对象,它是对setter方法对应
// Method 对象的封装
private final Map<String, Invoker> setMethods = new HashMap<>();
//属性相应的getter方法集合,key 是属性名称,value 也是Invoker对象
private final Map<String, Invoker> getMethods = new HashMap<>();
//记录了属性相应的setter方法的参数值类型,key是属性名称,value是setter方法的参数类型
private final Map<String, Class<?>> setTypes = new HashMap<>();
//记录了属性相应的getter方法的参数值类型,key是属性名称,value是getter方法的参数类型
private final Map<String, Class<?>> getTypes = new HashMap<>();
//记录了默认的构造函数
private Constructor<?> defaultConstructor;
//记录了所有属性名称的集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
在Reflector的构造函数中,会解析指定的Class对象,并填充上述集合,代码如下:
public Reflector(Class<?> clazz) {
type = clazz; //初始化type字段
//查找默认构造函数(无参构造函数)具体实现是以反射遍历所有构造方法
addDefaultConstructor(clazz);
//处理class中的getter方法
addGetMethods(clazz);
//处理class中的setter方法
addSetMethods(clazz);
//处理没有getter或setter方法的字段
addFields(clazz);
// 初始化为空数组
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
// 初始化为空数组
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
//初始化caseInsensitivePropertyMap集合 其中记录了所有大写格式的属性名称
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
Reflector.addGetMethods()方法主要解析类中定义的getter方法,Reflector.addSetMethods()方法主要解析类中定义的setter方法,两个类似,下面我们就以Reflector.addGetMethods()方法为例进行介绍,Reflector.addGetMethods()解析有下面三个步骤:
-
首先调用Reflector.getClassMethods()方法获取当前类以及父类中定义的所有方法的唯一签名以及相应的Method对象。
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()); } //获取父类继续while循环 currentClass = currentClass.getSuperclass(); } Collection<Method> methods = uniqueMethods.values(); //转换成methods数组返回 return methods.toArray(new Method[0]); }
在Reflector.getClassMethods()方法中会为每个方法生成唯一签名,并记录到conflictingSetters集合中,实现如下:
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
for (Method currentMethod : methods) {
if (!currentMethod.isBridge()) {
//通过getSignature()方法得到的方法签名 规则:返回值类型#方法名称:参数类型列表。
//通过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);
}
}
}
}
-
然后按照Java规范,从Reflector.getClassMethods()方法返回的Method数组中查找该类的getter方法,记录到conflictingSetters集合中。【该集合为
HashMap<String,<List<Method>>>
,key为属性名称,value是该属性对应的setter集合。】 -
当子类覆盖父类的getter方法且返回值发生变化时,在步骤【1】中就会产生两个签名不同的方法,在Reflector.addUniqueMethods()方法中会被当做两个不同的方法添加到conflictingSetters集合中,这显然不是我们要的结果,所以第三步我们会调用Reflector.resolveSetterConflicts()方法对这个覆写的方法进行处理,同时会将得到的getter方法记录到getMethods集合,并将其返回值类型填充到getTypes集合。Reflector.resolveSetterConflicts()方法具体实现如下:
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
// 遍历 conflictingGetters 集合
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
//获取属性名
String propName = entry.getKey();
//是否是不明确的
boolean isAmbiguous = false;
//
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)) {
//不明确的类型 【不明确 抛异常】
isAmbiguous = true;
break;
} else if (candidate.getName().startsWith("is")) {
//对于boolean类型
//当前方法返回值是当前最合适的方法的返回值的子类
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
//当前最适合的方法的返回值是当前方法返回值的子类,什么都不做 当前最适合的方法 依然不变
// OK getter type is descendant
} else if (winnerType.isAssignableFrom(candidateType)) { //当前返回值是当前最适合的方法的返回值的
winner = candidate;
} else {
//不明确的类型 要抛异常
isAmbiguous = true;
break;
}
}
addGetMethod(propName, winner, isAmbiguous);
}
}
Reflector.addGetMethod()方法对getMethods集合和getTypes集合进行填充,具体实现代码如下:
private void addGetMethod(String name, Method method, boolean isAmbiguous) {
//对于不明确的方法 抛异常处理
MethodInvoker invoker = isAmbiguous
? new AmbiguousMethodInvoker(method, MessageFormat.format(
"Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
name, method.getDeclaringClass().getName()))
: new MethodInvoker(method);
//将属性名以及对应的MethodInvoker对象添加到getMethods集合中,Invoker的内容后面解析
getMethods.put(name, invoker);
//获取返回值的Type,TypeParameterResolver会在后面分析
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
//将属性名及其getter方法的返回值加到getTypes集合中保存,typeToClass()方法后面分析
getTypes.put(name, typeToClass(returnType));
}
分析完Reflector.addGetMethods()的三个核心步骤后,我们看下具体实现代码,如下:
private void addGetMethods(Class<?> clazz) {
//conflictingGetters集合 key为属性名称,value为相应的getter方法集合,因为子类可能覆盖父类的getter方法,
//所以同一属性的名称可能存在多个getter方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//步骤一:获取指定类以及父类和接口中定义的方法
Method[] methods = getClassMethods(clazz);
//步骤二:按照javabean规范查找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);
Reflector.addFields()方法会处理类中定义的所有字段,并且将处理的字段信息添加到setMethods集合、setTypes集合、getMethods集合、getTypes集合中,Reflector.addFileds()方法的实现代码如下:
//处理类中定义的所有字段
private void addFields(Class<?> clazz) {
//提取clazz中定义的所有字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
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();
//过滤调static和final修饰的方法
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
//填充setMethods集合和setTypes集合
addSetField(field);
}
}
//getMethods集合中不包同名属性时,将其记录到getMethods集合和getTypes集合中
if (!getMethods.containsKey(field.getName())) {
//填充getMethods集合和getTypes集合
addGetField(field);
}
}
if (clazz.getSuperclass() != null) {
//处理父类中定义的字段
addFields(clazz.getSuperclass());
}
}
Reflector中提供多个get *()方法用于读取上述集合中的元数据信息。
3. Invoker & GetFieldInvoker & SetFieldInvoker & AmbiguousMethodInvoker & MethodInvoker
add *Methods()方法和add *Field()方法在上述集合中添加元素的时候,会将getter/setter方法对应的Method对象以及字段对应的Field对象统一封装成Invoker对象。Invoker接口的定义如下:
public interface Invoker {
//调用指定字段的值或者指定的方法
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
//返回属性相应的类型
Class<?> getType();
}
invoker接口的实现如下图:
<img src="http://qiniu-cdn.janker.top/oneblog/20200110135144701.jpg" style="zoom: 33%;" />
GetFieldInvoker/SetFieldInvoker通过field字段封装了FIeld对象,两者的invoke()方法是通过调用Field.get()/set()方法实现的。MethodInvoker通过method字段封装了对应的Method对象,其invoke()方法是通过调用Method.invoke()方法实现的。
4. ReflectorFactory
ReflectorFactory接口主要实现了对Reflector对象的创建和缓存,接口定义如下:
public interface ReflectorFactory {
//检查该ReflectorFactory对象是否会缓存Reflector对象
boolean isClassCacheEnabled();
//设置是否缓存Reflector对象
void setClassCacheEnabled(boolean classCacheEnabled);
//创建指定对象的Reflector对象
Reflector findForClass(Class<?> type);
}
MyBatis对ReflectorFactory接口只提供了DefaultReflectorFactory这一个实现类,他与Reflector关系如图:
<img src="http://qiniu-cdn.janker.top/oneblog/20200110143413148.jpg" style="zoom:50%;" />
DefaultReflectorFactory的字段含义如下:
//该字段决定是否对Reflector对象的缓存
private boolean classCacheEnabled = true;
//使用ConcurrentHashMap集合实现对Reflector对象的缓存
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
DefaultReflectorFactory提供findForClass()方法实现为指定Class创建Reflector对象,并将Reflector对象缓存到reflectorMap集合中,具体代码如下:
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) { //检测是否开启缓存
//cas放入缓存
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
//未开启缓存则直接创建
return new Reflector(type);
}
}
此外除了使用MyBatis提供的DefaultReflectorFactory实现,们还可以在mybatis-config.xml中配置自定义的ReflectorFactory实现类,从而实现上面功能的拓展。(后面会在介绍MyBatis初始化流程时,会提到拓展点。)
5. TypeParameterResolver
1. Type知识准备
在分析TypeParameterResolver
之前我们先来看下Type接口,Type是所有类型的父接口,他有四个接口和一个实现类,类图如图所示:
<img src="http://qiniu-cdn.janker.top/oneblog/20200110180942598.png" style="zoom:33%;" />
Class
Class
:表示原始类型类型,Class对象表示JVM中一个类或者接口,每个Java类在JVM都表现为一个Class对象。在程序中可以通过 ”xxx.class“,”对象.getClass()"或者是Class.forName("类名")来获取Class对象。数组也被映射为Class对象,所有元素类型相同切维数相同的数组都共享一个Class对象。
ParameterizedType
ParameterizedType:表示参数化类型,例如List< String >,Map<Integer,String>,Service< User >这种带有泛型的类型。
GenericArrayType
GenericArrayType
:表示的是数组类型且组成元素是ParameterizedType或者TypeVariable。例如List< String >[]或者T[]
TypeVariable
TypeVariable
:表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如在LIst<T>中的T就是类型变量,它在编译的时候需要被转化成一个具体的类型才能正常使用。
WildcardType
WildcardType
:表示通配符类型,例如:? extends Number
和? extends Integer
。
2. 方法解析
上面介绍了Type接口的基础知识,我们回来继续分析TypeParameterResolver
。在对Reflector
的分析中我们看到了TypeParameterResolver
的身影,他是一个工具类提供一些列静态方法来解析指定类中的字段、方法返回值或者方法参数的类型。TypeParameterResolver
中各个方法之间的调用大致如下:【其中有一些递归调用没有体现出来,在后面的分析中会进行强调。】
<img src="http://qiniu-cdn.janker.top/oneblog/20200110184635768.png" style="zoom: 50%;" />
TypeParameterResolver
中通过resolveFieldTypes()方法、resolveReturnTypes()方法、resolveParamTypes()方法分别解析字段类型、方法返回值类型和方法参数列表中各个参数的类型。这三个方法类似。我们就以resolveFieldTypes()来分析。TypeParameterResolve.resolveFieldTypes()
方法实现代码如下:
public static Type resolveFieldType(Field field, Type srcType) {
//获取字段的声明类型
Type fieldType = field.getGenericType();
//获取字段定义所在类的Class对象
Class<?> declaringClass = field.getDeclaringClass();
//调用resolveType()方法做后续处理
return resolveType(fieldType, srcType, declaringClass);
}
上述三个解析方法都会调用resolveType()会根据第一个参数Type,即方法、返回值或者方法返回参数的类型,选择合适的方法解析。resolveType()的第二个参数表示查找该字段、返回值或者方法参数的起始位置。第三个参数则表示该字段、方法所在的类。
TypeParameterResolve.resolveType()
方法代码如下:
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
if (type instanceof TypeVariable) {
//解析 TypeVariable 类型
return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
} else if (type instanceof ParameterizedType) {
//解析 ParameterizedType 类型
return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
} else if (type instanceof GenericArrayType) {
//解析 GenericArrayType 类型
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
} else {
return type;
}
}
TypeParameterResolve.resolveParameterizedType()
方法代码如下:
private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
// 得到参数化类型中的原始类型对应的Class对象
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
//返回参数化类型的类型变量
Type[] typeArgs = parameterizedType.getActualTypeArguments();
//用于保存解析后的结果
Type[] args = new Type[typeArgs.length];
for (int i = 0; i < typeArgs.length; i++) {
if (typeArgs[i] instanceof TypeVariable) {
//解析类型变量
args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
} else if (typeArgs[i] instanceof ParameterizedType) {
//如果嵌套了ParameterizedType则调用resolveParameterizedType()方法进行处理
args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
} else if (typeArgs[i] instanceof WildcardType) {
//如果嵌套了WildcardType则调用resolveWildcardType()方法进行处理
args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
} else {
args[i] = typeArgs[i];
}
}
//将解析结果封装成TypeParameterResolver中定义的ParameterizedType实现并返回
return new ParameterizedTypeImpl(rawType, null, args);
}
TypeParameterResolve.resolveTypeVar()
方法负责解析TypeVariable。具体实现如下:
private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
Type result;
Class<?> clazz;
if (srcType instanceof Class) {
clazz = (Class<?>) srcType;
} else if (srcType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) srcType;
clazz = (Class<?>) parameterizedType.getRawType();
} else {
throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
}
if (clazz == declaringClass) {
//获取上界
Type[] bounds = typeVar.getBounds();
if (bounds.length > 0) {
return bounds[0];
}
return Object.class;
}
//获取声明的父类类型
Type superclass = clazz.getGenericSuperclass();
//通过扫描父类进行后续解析 这个是递归的入口
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
if (result != null) {
return result;
}
// 获取接口
Type[] superInterfaces = clazz.getGenericInterfaces();
for (Type superInterface : superInterfaces) {
//通过扫描接口进行后续解析 逻辑同扫描父类
result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
if (result != null) {
return result;
}
}
//若在某个集成结构中解析成功则返回Object.class
return Object.class;
}
scanSuperTypes()方法具体实现如下:
private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
if (superclass instanceof ParameterizedType) {
ParameterizedType parentAsType = (ParameterizedType) superclass;
//获取父类的原始类型
Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
if (srcType instanceof ParameterizedType) {
//转化父类参数化类型
parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
}
if (declaringClass == parentAsClass) {
for (int i = 0; i < parentTypeVars.length; i++) {
if (typeVar.equals(parentTypeVars[i])) {
return parentAsType.getActualTypeArguments()[i];
}
}
}
if (declaringClass.isAssignableFrom(parentAsClass)) {
//继续解析父类,知道解析到定义该字段的类
return resolveTypeVar(typeVar, parentAsType, declaringClass);
}
} else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
//声明的父类不再含有类型变量且不是定义该字段的类则继续解析
return resolveTypeVar(typeVar, superclass, declaringClass);
}
return null;
}
分析完TypeParameterResolver.resolveTypeVar()和resolveParameterizedType()两个方法后,再来看resolveGenericArrayType()方法,该方法负责解析GenericArrayType类型的变量,它会根据数组元素的类型选择合适的resolve *()方法进行解析,具体实现如下:
private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass) {
//获取数组元素的类型
Type componentType = genericArrayType.getGenericComponentType();
Type resolvedComponentType = null;
//根据数组元素类型选择合适的方法进行解析
if (componentType instanceof TypeVariable) {
resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
} else if (componentType instanceof GenericArrayType) {
//递归调用resolveGenericArrayType()方法
resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
} else if (componentType instanceof ParameterizedType) {
resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
}
//根绝解析后的数组项类型构造返回类型
if (resolvedComponentType instanceof Class) {
return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
} else {
return new GenericArrayTypeImpl(resolvedComponentType);
}
}
最后我们来分析一下TypeParameterResolver.resolveWildcardType()
方法。该方法负责解析WildcardType类型的变量。首先解析WildcardType类型的上下界,然后通过解析后的结果构造WildcardTypeImpl对象返回。具体解析过程与上述resolve *()类似。
通过上面的方法分析我们知道,当存在复杂的继承关系以及泛型类型时,TypeParameterResolver可以帮助我们解析字段、方法和方法返回值的类型。这个是Reflector类的基础。
MyBatis源码中也提供了TypeParameterResolver相关的TypeParameterResolverTest这个测试类,可以从多方面测试TypeParameterResolver的功能。我们可以参考一下更好的了解TypeParameterResolver的功能。
6. ObjectFactory
MyBatis中有很多模块会使用到ObjectFactory这个接口,该接口提供了多个crate()方法的重载,通过这些方法可以创建指定类型的对象。ObjectFactory接口定义如下:
public interface ObjectFactory {
//设置配置信息
default void setProperties(Properties properties) {
// NOP
}
//通过无参构造器创建指定类的对象
<T> T create(Class<T> type);
//根据参数列表,从指定类型中选择合适的构造器创建对象
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
//检测指定类型是否为集合类型,主要处理java.util.Collection及其子类
<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();
try {
return constructor.newInstance();
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance();
} else {
throw e;
}
}
}
//根据指定的参数列好查找构造函数,并实例化对象
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
try {
return constructor.newInstance(constructorArgs.toArray(new Object[0]));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance(constructorArgs.toArray(new Object[0]));
} else {
throw e;
}
}
} catch (Exception e) {
String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
.stream().map(Class::getSimpleName).collect(Collectors.joining(","));
String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
.stream().map(String::valueOf).collect(Collectors.joining(","));
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
除了使用MyBatis提供的DefaultObjectFactory实现,我们还可以在mybatis-config.xml配置文件中指定自定义的ObjectFactory接口实现类,从而实现功能上的拓展。
7. Property工具集
下面我们介绍反射模块使用到的三个属性工具类,分别是PropertyTokenizer
、PropertyNamer
和PropertyCopier
。
PropertyTokenizer
PropertyTokenizer中各个字段含义如下:
//当前表达式的名称
private String name;
//当前表达式的索引名
private final String indexedName;
//索引下标
private String index;
//子表达式
private final String children;
在PropertyTokenizer的构造函数中会对传入的表达式进行分析,并初始化上面的属性字段,实现如下:
public PropertyTokenizer(String fullname) {
//查找"."的位置
int delim = fullname.indexOf('.');
if (delim > -1) {
//初始化name
name = fullname.substring(0, delim);
//初始化children
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
//初始化indexedName
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
//初始化index
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
PropertyTokenizer继承了Iterator接口,它可以迭代处理嵌套多层表达式。PropertyTokenizer.next()会创建新的PropertyTokenizer对象并解析children字段记录的子表达式。
PropertyNamer
PropertyNamer是另一个工具类,将提供了下列静态方法帮助完成方法名到属性名的转换,以及多种检测操作。
public final class PropertyNamer {
private PropertyNamer() {
// Prevent Instantiation of Static Class
}
//该方法会将方法名转换成属性
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;
}
// 检测是否是对应属性名
public static boolean isProperty(String name) {
return isGetter(name) || isSetter(name);
}
//检测是否为getter方法
public static boolean isGetter(String name) {
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
}
//检测是否为setter方法
public static boolean isSetter(String name) {
return name.startsWith("set") && name.length() > 3;
}
}
PropertyCopier
PropertyCopier是一个属性拷贝的工具类。其核心方法是copyBeanProperties()方法,主要实现相同类型的两个对象之间的属性拷贝,具体实现如下:
public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
Class<?> parent = type;
while (parent != null) {
final Field[] fields = parent.getDeclaredFields();
for (Field field : fields) {
try {
try {
field.set(destinationBean, field.get(sourceBean));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
//将sourceBean对象中的属性设置到destinationBean对象中
field.set(destinationBean, field.get(sourceBean));
} else {
throw e;
}
}
} catch (Exception e) {
// Nothing useful to do, will only fail on final fields, which will be ignored.
}
}
// 继续拷贝父类中的定义的字段
parent = parent.getSuperclass();
}
}
8. MetaClass
MetaClass通过Reflector和PropertyTokenizer组合使用,实现了对复杂的属性表达式的解析,并实现了获取指定属性信息的功能。MetaClass中各个字段的含义如下:
//用于缓存Reflector对象
private final ReflectorFactory reflectorFactory;
//在创建MetaClasyos时会指定一个类,该Reflector对象会用于记录该类相关的元数据
private final Reflector reflector;
MetaClass的构造函数会为指定的Class创建对应的Reflector对象,并用其初始化MetaClass.reflector字段,具体代码如下:
//MetaClass的构造方法是使用private修饰的
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
//创建Reflector对象
this.reflector = reflectorFactory.findForClass(type);
}
//使用静态方法创建MetaClass对象
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
MetaClass中比较重要的是findProperty()方法,他是通过MetaClass.buildProperty()方法实现的,而buildProperty()方法会通过PropertyTokenizer解析更复杂的属性表达式,具体实现如下:
public String findProperty(String name) {
//委托给buildProperty()方法实现
StringBuilder prop = buildProperty(name, new StringBuilder());
return prop.length() > 0 ? prop.toString() : null;
}
private StringBuilder buildProperty(String name, StringBuilder builder) {
//解析属性表达式
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) { //是否有子表达式
//查找PropertyTokenizer.name对应的属性
String propertyName = reflector.findPropertyName(prop.getName());
if (propertyName != null) {
//追加属性名
builder.append(propertyName);
builder.append(".");
//为该属性创建MataClass对象
MetaClass metaProp = metaClassForProperty(propertyName);
//递归解析PropertyTokenizer.children字段,并将解析结果添加到builder中保存
metaProp.buildProperty(prop.getChildren(), builder);
}
} else {
//递归出口
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
public MetaClass metaClassForProperty(String name) {
//查找指定属性对应的class
Class<?> propType = reflector.getGetterType(name);
//为改属性创建对应的MetaClass对象
return MetaClass.forClass(propType, reflectorFactory);
}
注意:Meta.Class.findProperty()方法只查找"."导航的属性,并没有检测下标。
MetaClass.hasGetter()
方法和MetaClass.hasSetter()
方法负责判断属性表达式所表示的属性是否有对应的属性,这两个方法逻辑类似,我们以hasGetter()
方法为例子进行分析,这两个类方法实现最终都会查找Reflector.getMethods
集合或Reflector.setMethods
集合。根据前面介绍的Reflector.addFields()
方法,当字段没有对应的getter/setter方法时会添加相应的getFieldInvoker/setFieldInvoker
对象,所以Reflector有权限访问指定字段的时候这两个方法的行为并不像其方法所暗示的那样只直接判断属性的getter/setter方法,我们来看看MetaClass.hasGetter()
方法的实现代码:
public boolean hasGetter(String name) {
//解析属性表达式
PropertyTokenizer prop = new PropertyTokenizer(name);
//存在待处理的子表达式
if (prop.hasNext()) {
//PropertyTokenizer.name指定的属性有getter方法 才能处理子表达式
if (reflector.hasGetter(prop.getName())) {
//metaClassForProperty(PropertyTokenizer)是上面metaClassForProperty(String)的重载 但是逻辑有很大差异
MetaClass metaProp = metaClassForProperty(prop);
//递归入口
return metaProp.hasGetter(prop.getChildren());
} else {
//递归出口
return false;
}
} else {
//递归出口
return reflector.hasGetter(prop.getName());
}
}
MetaClass.metaClassForProperty(PropertyTokenizer)方法底层会调用MetaClass.getGetterType(PropertyTokenizer)方法,针对PropertyTokenizer中是否包含索引信息进一步处理,代码如下:
private MetaClass metaClassForProperty(PropertyTokenizer prop) {
//获取表达式标识的属性的类型
Class<?> propType = getGetterType(prop);
return MetaClass.forClass(propType, reflectorFactory);
}
private Class<?> getGetterType(PropertyTokenizer prop) {
//获取属性类型
Class<?> type = reflector.getGetterType(prop.getName());
//该表达式中是否试用"[]"指定了下标,切是Collect子类
if (prop.getIndex() != null && Collection.class.isAssignableFrom(type)) {
//通过TypeParameterResolver工具类解析属性的类型
Type returnType = getGenericGetterType(prop.getName());
//针对ParameterizedType进行处理,既针对泛型类型进行处理
if (returnType instanceof ParameterizedType) {
//获取实际的类型参数
Type[] actualTypeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
//泛型的类型
returnType = actualTypeArguments[0];
if (returnType instanceof Class) {
type = (Class<?>) returnType;
} else if (returnType instanceof ParameterizedType) {
type = (Class<?>) ((ParameterizedType) returnType).getRawType();
}
}
}
}
return type;
}
private Type getGenericGetterType(String propertyName) {
try {
//根据Reflector.getMethods集合中记录的invoker实现类的类型,决定解析getter方法返回值类型还是解析字段类型
Invoker invoker = reflector.getGetInvoker(propertyName);
if (invoker instanceof MethodInvoker) {
Field _method = MethodInvoker.class.getDeclaredField("method");
_method.setAccessible(true);
Method method = (Method) _method.get(invoker);
return TypeParameterResolver.resolveReturnType(method, reflector.getType());
} else if (invoker instanceof GetFieldInvoker) {
Field _field = GetFieldInvoker.class.getDeclaredField("field");
_field.setAccessible(true);
Field field = (Field) _field.get(invoker);
return TypeParameterResolver.resolveFieldType(field, reflector.getType());
}
} catch (NoSuchFieldException | IllegalAccessException ignored) {
}
return null;
}
MetaClass中的其他get *()
方法比较简单,大多数直接依赖Reflector的对应方法的实现的。通过对MetaClass的分析,我们了解了findProperty()
、hasGetter()
、hasSetter()
等方法的实现原理。
9. ObjectWrapper
ObjectWrapper顾名思义就是对象的包装类,对对象级别的元信息进行处理。ObjectWrapper接口是对对象的包装,抽象了对象的属性信息,它定义了一系列的查询对属性信息的方法,以及更新属性的方法。
ObjectWrapper接口的定义如下:
public interface ObjectWrapper {
//如果是普通bean调用getter方法 如果是集合 则获取指定key或者下标对应的value值
Object get(PropertyTokenizer prop);
//如果是普通bean调用setter方法 如果是集合 则设置指定key或者下标对应的value值
void set(PropertyTokenizer prop, Object value);
//查找属性表达式指定的属性,第二个参数标识是否忽视属性表达式的下划线
String findProperty(String name, boolean useCamelCaseMapping);
//查找可读属性的名称集合
String[] getGetterNames();
//查找可写属性的名称集合
String[] getSetterNames();
//解析表达式指定属性的setter方法的参数类型
Class<?> getSetterType(String name);
//解析表达式指定属性的getter方法的参数类型
Class<?> getGetterType(String name);
//判断属性表达式指定属性是否有getter方法
boolean hasSetter(String name);
//判断属性表达式指定属性是否有setter方法
boolean hasGetter(String name);
//为属性表达式指定的属性创建相应的MetaObject对象
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
//封装的对象是否为Collect类型
boolean isCollection();
//调用Collection对象的add()方法
void add(Object element);
//调用Collection对象的addAll()方法
<E> void addAll(List<E> element);
}
ObjectWrapperFactory负责创建ObjectWrapper对象,关系如图所示:
<img src="http://qiniu-cdn.janker.top/oneblog/20200111223939490.jpg" style="zoom:67%;" />
DefaultObjectWrapperFactory实现了ObjectWrapperFactory接口,但它实现的getWrapperFor()方法始终抛出异常,hasWrapperFor()方法始终返回false,所以该实现方法实际上是不可用的。但是与ObjectFactory类似,我们可以在mybatis-config.xml配置自定义的ObjectWrapperFactory实现类进行拓展。
BaseWrapper是一个实现了ObjectWrapper接口的抽象类,其中封装了MetaObject对象,并提供了三个常用的方法供其子类使用。如图所示:
<img src="http://qiniu-cdn.janker.top/oneblog/20200111225050400.jpg" style="zoom:50%;" />
BaseWrapper.resolveCollection()方法会调用MetaObject.getValue()方法,它会解析属性表达式并获取指定的属性,MetaObject.getValue()方法的实现在下面介绍。
BaseWrapper.getCollectionValue()方法和setCollectionValue()方法会解析属性表达式的索引信息,然后获取或设置对应项。这两个方法类似。在这里我们只分析一下getCollectionValue()方法。
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {
//如果是map类型则index为key
return ((Map) collection).get(prop.getIndex());
} else {
//如果是其他类型则index为下标
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
return ((List) collection).get(i);
} else if (collection instanceof Object[]) {
return ((Object[]) collection)[i];
} else if (collection instanceof char[]) {
return ((char[]) collection)[i];
} else if (collection instanceof boolean[]) {
return ((boolean[]) collection)[i];
} else if (collection instanceof byte[]) {
return ((byte[]) collection)[i];
} else if (collection instanceof double[]) {
return ((double[]) collection)[i];
} else if (collection instanceof float[]) {
return ((float[]) collection)[i];
} else if (collection instanceof int[]) {
return ((int[]) collection)[i];
} else if (collection instanceof long[]) {
return ((long[]) collection)[i];
} else if (collection instanceof short[]) {
return ((short[]) collection)[i];
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
BeanWrapper继承了BaseWrapper抽象类,其中封装了一个JavaBean对象以及该JavaBean相应的MetaClass对象,当然还有从BaseWrapper继承下来的、该JavaBean对象相应的MetaObject对象。
BeanWrapper.get()方法和set()方法会根据指定的属性表达式,获取或者设置相应的属性值,两者逻辑相似,这里我们以get()方法分析,具体代码如下:
public Object get(PropertyTokenizer prop) {
//存在索引信息 则表示属性表达式中的name部分为集合类型
if (prop.getIndex() != null) {
//通过MetaObject.getValue()方法获取object对象中指定集合属性
Object collection = resolveCollection(prop, object);
//获取集合元素
return getCollectionValue(prop, collection);
} else {
//不存在索引信息 则name部分为普通对象,查找并调用invoker相关方法获取属性
return getBeanProperty(prop, object);
}
}
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
//根据属性名称查找Reflector.getMethods集合中相应的getFieldInvoker或者MethodInvoker
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
//获取属性值
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
}
}
CollectionWrapper实现了ObjectWrapper接口,其中封装了Collection< Object >类型的对象,但是它大部分实现方法都会抛出UnsupportedOperationException异常。
MapWrapper是BaseWrapper的另一个实现类,其中封装了Map<String,Object>类型对象,我们了解了MetaObject和BeanWrapper实现后,轻而易举就能看懂MapWrapper的实现代码。
10. MetaObject
MetaObject提供了获取/设置对象中指定的属性值、检测getter/setter等常用功能,但是ObjectWrapper只是这些功能的最后一站,我们省略了对属性表达式解析过程的介绍,而该解析过程就是在MetaObject中实现的。
MetaObject中字段的含义如下:
//原始JavaBean对象
private final Object originalObject;
//封装了originalObject对象
private final ObjectWrapper objectWrapper;
//负责实例化originalObject的工厂对象
private final ObjectFactory objectFactory;
//负责创建ObjectWrapper的工厂对象
private final ObjectWrapperFactory objectWrapperFactory;
//用于创建并缓存Reflector对象的工厂对象
private final ReflectorFactory reflectorFactory;
MetaObject的构造方法会根据传入的原始对象的类型以及ObjectFactory工厂的实现创建相应的ObjectWrapper对象,代码如下:
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
//初始化上述字段
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
//若原始对象已经是ObjectWrapper对象则直接使用
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
//若objectWrapperFactory能够为该原始兑现创建对应的ObjectWrapper对象则由优先使用objectWrapperFactory,
//而DefaultObjectWrapperFactory.hasWrapperFor()始终返回false,用户可以自定义ObjectWrapperFactory实现进行拓展
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
//若原始对象是map类型 则创建MapWrapper对象
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
//若原始对象是Collection类型 则创建CollectionWrapper对象
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
//若对象是普通的JavaBean对象,则创建BeanWrapper对象
this.objectWrapper = new BeanWrapper(this, object);
}
}
//Meta的构造方法是private的 只能靠forObject()这个静态方法来创建MetaObject对象
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
//若原始对象为空 则统一返回SystemMetaObject.NULL_META_OBJECT这个静态对象
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
MetaObject和ObjectWrapper中关于类级别的方法,例如hasGetter()、hasSetter()、findProperty()等方法,都是直接调用MetaClass的对应方法实现的。其他方法都是关于对象级别的方法,这些方法都是与ObjectWrapper配合实现,例如MetaObject.getValue()/setValue()方法,这里以getValue()方法为例进行分析,具体代码如下:
public Object getValue(String name) {
//解析属性表达式
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) { //处理子表达式
//根据PropertyTokenizer解析后制定的属性 创建相应的MetaObject对象
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
//递归处理子表达式
return metaValue.getValue(prop.getChildren());
}
} else {
//通过ObjectWrapper获取指定的属性值
return objectWrapper.get(prop);
}
}
public MetaObject metaObjectForProperty(String name) {
//获取指定的属性
Object value = getValue(name);
//创建该属性对象相应的MetaObject对象
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
ObjectWrapper.instantiateProperty()方法实际上就是调用ObjectFactory接口的create()方法(默认实现是DefaultObjectFactory)创建对象并将其设置到所属的对象中,这里我们看下具体的实现代码:
public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
MetaObject metaValue;
//获取属性相应的setter方法的参数类型
Class<?> type = getSetterType(prop.getName());
try {
//通过反射的方式创建对象
Object newObject = objectFactory.create(type);
metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
//将上面创建的属性对象 设置到对应的属性对象集合中
set(prop, newObject);
} catch (Exception e) {
throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);
}
return metaValue;
}
理解了MetaObject和BeanWrapper如何通过递归的方式处理属性表达式指定的属性值后,其他方法的实现原理就好理解了。例如getGetterType()、getSetterType()、hasGetter()、hasSetter()等方法,都是先递归处理属性表达式,然后调用MetaClass相应的方法实现的。
本文由 Janker 创作,采用 CC BY 3.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
<img src="http://qiniu-cdn.janker.top/oneblog/20200311150833864.jpg" style="zoom:50%;" />