MyBatis
在进行参数处理、结果映射时等操作时,会涉及大量的反射操作。为了简化这些反射相关操作,MyBatis
在 org.apache.ibatis.reflection
包下提供了专门的反射模块,对反射操作做了近一步封装,提供了更为简洁的 API
。
一、JavaBean规范
JavaBean 具有如下特征:
- 所有的属性都是私有的(通过 getter 和 setter 访问)
- 拥有公有的无参构造函数
- 提供 setter/getter
- 实现 Serializable 接口
二、Reflector 和 ReflectorFactory
2.1 Reflector
出于性能方面的考虑,Mybatis 不是等到使用的时候去解析 XML/反射类,而是为每一个类提供了反射器类 Reflector ,该类中存储了反射需要使用的类的元信息。
MyBatis
提供 Reflector
类来缓存类的字段名和 getter/setter 方法的元信息,使得反射时有更好的性能。使用方式是将原始类对象传入其构造方法,生成 Reflector
对象。
Reflector 是 MyBatis 中反射模块的基础,每个 Reflector 对象都对应一个类,在 Reflector 中 缓存了反射操作需要使用的类的元信息。 Reflector 中各个字段的含义如下:
private final Class<?> type;//对于class的类型
//可读属性的名称集合,存在get方法即可读
private final String[] readablePropertyNames;
//可写属性的名称集合,存在set方法即可写
private final String[] writablePropertyNames;
//记录了属性相应的 setter 方法, key 是属性名称, value 是 Invoker 对象,它是对 setter 方法对应
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对象
//查找 clazz 的默认构造方法(元参构造方法),具体实现是通过反射遥历所有构造方法,代码并不复杂
addDefaultConstructor(clazz);
//处理clazz中的get方法信息,填充getMethods、getTypes
addGetMethods(clazz);
//处理clazz中的set方法信息,填充setMethods、setTypes
addSetMethods(clazz);
//处理没有get、set方法的属性
addFields(clazz);
//根据get、set方法初始化可读属性集合和可写属性集合
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 对应着一个 Class 对象
记录了默认构造函数
其余的是属性及其 setter/gettter 相关
对于一个属性(只有有 setter/getter 才能被称之为属性)
(1)如果是可读的(有 getter 方法),则 Reflector 会将其及其方法处理后放入对应的可读相关的集合中;
(2)如果是可写的(有 setter 方法),则 Reflector 会将其及其方法处理后放入对应的可写相关的集合中;
(3)最后使用 Map<String, String> caseInsensitivePropertyMap 记录所有的属性。
addGetMethods
和 addSetMethods
分别获取类的所有方法,从符合 getter/setter
规范的方法中解析出字段名,并记录方法的参数类型、返回值类型等信息:
private void addGetMethods(Class<?> clazz) {
//字段名-get方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//获取类的所有方法,及其实现接口的方法,并根据方法签名去重
Method[] methods = getClassMethods(clazz);
//取get 方法 条件是入参为空,且已get开头
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveGetterConflicts(conflictingGetters);
}
public static boolean isGetter(String name) {
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
}
private void addSetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingSetters = new HashMap<>();
Method[] methods = getClassMethods(clazz);
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveSetterConflicts(conflictingSetters);
}
对 getter/setter
方法进行去重是通过类似 java.lang.String#getSignature:java.lang.reflect.Method
的方法签名来实现的,如果子类在实现过程中,参数、返回值使用了不同的类型(使用原类型的子类),则会导致方法签名不一致,同一字段就会对应不同的 getter/setter
方法,因此需要进行去重
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
// 属性名
String propName = entry.getKey();
for (Method candidate : entry.getValue()) {
if (winner == null) {
winner = candidate;
continue;
}
// 字段对应了多个get方法
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")) {
// 返回值为boolean的get方法可能有多个,如getIsSave和isSave,优先取is开头的
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
// 可能会出现接口中的方法返回值是List,子类实现方法返回值是ArrayList,使用子类返回值方法
} 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.");
}
}
// 记录字段名对应的get方法对象和返回值类型
addGetMethod(propName, winner);
}
}
去重的方式是使用更规范的方法以及使用子类的方法。在确认字段名对应的唯一 getter/setter
方法后,记录方法名对应的方法、参数、返回值等信息。MethodInvoker
可用于调用 Method
类的 invoke
方法来执行 getter/setter
方法(addSetMethods
记录映射关系的方式与 addGetMethods
大致相同)。
private void addGetMethod(String name, Method method) {
// 过滤$开头、serialVersionUID的get方法和getClass()方法
if (isValidPropertyName(name)) {
// 字段名-对应get方法的MethodInvoker对象
getMethods.put(name, new MethodInvoker(method));
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 字段名-运行时方法的真正返回类型
getTypes.put(name, typeToClass(returnType));
}
}
接下来会执行 addFields
方法,此方法针对没有 getter/setter
方法的字段,通过包装为 SetFieldInvoker
在需要时通过 Field
对象的反射来设置和读取字段值。
private void addFields(Class<?> 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();
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
// 非final的static变量,没有set方法,可以通过File对象做赋值操作
addSetField(field);
}
}
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
if (clazz.getSuperclass() != null) {
// 递归查找父类
addFields(clazz.getSuperclass());
}
}
2.2 Invoker
Invoker
接口用于抽象设置和读取字段值的操作。对于有 getter/setter
方法的字段,通过 MethodInvoker
反射执行;对应其它字段,通过 GetFieldInvoker
和 SetFieldInvoker
操作 Field
对象的 getter/setter
方法反射执行。
public interface Invoker {
/**
* 通过反射设置或读取字段值
*
* @param target
* @param args
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
/**
* 字段类型
*
* @return
*/
Class<?> getType();
}
- MethodInvoker:有
getter/setter
方法的字段,方法的 Invoker - GetFieldInvoker:如果没有
getter
,则使用该方法,通过Filed
类直接读取成员变量的值 - SetFieldInvoker:如果没有
setter
,则使用该方法,通过Filed
类直接设置成员变量的值
2.3 TypeParameterResolver
在开始介绍 TypeParameterResolver 之前, 先简单介绍一下 Type 接口的基础知识。 Type 是所有类型的父接口,它有四个子接口和一个实现类。
Class
:原始类型,Class 类的对象表示 JVM 中的一个类或接口, 每个 Java 类在 JVM 里都表现为一个 Class 对象。ParameterizedType
:泛型类型,如:List<String>
TypeVariable
:泛型类型变量,如:List<T>
中的T
GenericArrayType
:组成元素是ParameterizedType
或TypeVariable
的数组类型,如:List<String>[]
、T[]
WildcardType
:通配符泛型类型变量,如:List<?>
中的 ?
在对 Reflector 的 分析过程中,我们看到了 TypeParameterResolver 的身影,它是一个工具类,提供了一系列静态 方法来解析指定类中的宇段、方法返回值或方法参数的类型。 TypeParameterResolver 中各个静 态方法之间的调用关系大致如图 所示,为保持清晰,其中递归调用没有表现出来,在后面 的代码分析过程中会进行强调,
TypeParameterResolver
分别提供 resolveFieldType
、resolveReturnType
、resolveParamTypes
方法用于解析字段类型、方法返回值类型和方法入参类型,这些方法均调用 resolveType
来获取类型信息:
/**
* 获取类型信息
*
* @param type 根据是否有泛型信息签名选择传入泛型类型或简单类型
* @param srcType 引用字段/方法的类(可能是子类,字段和方法在父类声明)
* @param declaringClass 字段/方法声明的类
* @return
*/
private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
if (type instanceof TypeVariable) {
// 泛型类型变量,如:List<T> 中的 T
return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
} else if (type instanceof ParameterizedType) {
// 泛型类型,如:List<String>
return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
} else if (type instanceof GenericArrayType) {
// TypeVariable/ParameterizedType 数组类型
return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
} else {
// 原始类型,直接返回
return type;
}
}
resolveTypeVar
用于解析泛型类型变量参数类型,如果字段或方法在当前类中声明,则返回泛型类型的上界或 Object
类型;如 果在父类中声明,则递归解析父类;父类也无法解析,则递归解析实现的接口
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) {
// 泛型类型,如 TestObj<String>
ParameterizedType parameterizedType = (ParameterizedType) srcType;
// 取原始类型TestObj
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) {
// 返回泛型类型变量上界,如:T extends String,则返回String
return bounds[0];
}
// 没有上界返回Object
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;
}
}
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 == parentTypeVars[i]) {
// 使用变量对应位置的真正类型(可能已经被替换),如父类 A<T>,子类 B extends A<String>,则返回String
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;
}
解析方法返回值和方法参数的逻辑大致与解析字段类型相同,MyBatis
源码的TypeParameterResolverTest
类提供了相关的测试用例。
2.4 ReflectorFactory
看名称,工厂方法,是为了创建个缓存 Reflector。MyBatis
还提供 ReflectorFactory
接口用于实现 Reflector
容器,其默认实现为 DefaultReflectorFactory
,其中可以使用 classCacheEnabled
属性来配置是否使用缓存。
/**
* 创建Reflector的工厂接口
*/
public interface ReflectorFactory {
/**
* 检测 ReflectorFactory 对象是否会缓存 Reflector 对象
*/
boolean isClassCacheEnabled();
/**
* 设置是否缓存
*/
void setClassCacheEnabled(boolean classCacheEnabled);
/**
* 缓存中查找 Class 对应的 Reflector 对象,找不到则创建
*/
Reflector findForClass(Class<?> type);
}
mybatis 为我们提供了该方法的默认实现 DefaultReflectorFactory
。该类的实现很简单,就是通过 ConcurrentMap<Class<?>, Reflector>
对 Reflector
进行缓存。
2.5 ObjectFactory
ObjectFactory
接口是 MyBatis
对象创建工厂,其默认实现 DefaultObjectFactory
通过构造器反射创建对象,支持使用无参构造器和有参构造器。
MyBatis 中有很多模块会使用到 ObjectFactory 接口,该接口提供了多个 create()方法的重载, 通过这些 create()方法可以创建指定类型的对象。 ObjectFactory 接口的定义如下:
public interface ObjectFactory {
/**
* 设置配置信息
* @param properties
*/
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()方法会 根据传入的参数列表选择合适的构造函数实例化对象,具体实现如下:
public class DefaultObjectFactory implements ObjectFactory, Serializable {
private static final long serialVersionUID = -8855120656740914948L;
//通过无参构造器创建对象
@Override
public <T> T create(Class<T> type) {
return create(type, null, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
//对象类型
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
//最终创建对象的方法
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[constructorArgTypes.size()]));
try {
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
constructor.setAccessible(true);
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} 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);
}
}
protected Class<?> resolveInterface(Class<?> type) {
Class<?> classToCreate;
if (type == List.class || type == Collection.class || type == Iterable.class) {
classToCreate = ArrayList.class;
} else if (type == Map.class) {
classToCreate = HashMap.class;
} else if (type == SortedSet.class) { // issue #510 Collections Support
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
return classToCreate;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
2.6 Property 工具集
反射模块中使用到的三个属性工具类,分别是 PropertyTokenizer 、 PropertyNamer
、PropertyCopier。
MyBatis
在映射文件定义 resultMap
支持如下形式:
<resultMap id="map" type="Order">
<result property="orders[0].items[0].name" column="col1"/>
<result property="orders[0].items[1].name" column="col2"/>
...
</resultMap>
rders[0].items[0].name
这样的表达式是由 PropertyTokenizer
解析的,其构造方法能够对表达式进行解析;同时还实现了 Iterator
接口,能够迭代解析表达式。
public PropertyTokenizer(String fullname) {
// orders[0].items[0].name
int delim = fullname.indexOf('.');
if (delim > -1) {
// name = orders[0]
name = fullname.substring(0, delim);
// children = items[0].name
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
// orders[0]
indexedName = name;
delim = name.indexOf('[');
if (delim > -1) {
// 0
index = name.substring(delim + 1, name.length() - 1);
// order
name = name.substring(0, delim);
}
}
/**
* 是否有children表达式继续迭代
*
* @return
*/
@Override
public boolean hasNext() {
return children != null;
}
/**
* 分解出的 . 分隔符的 children 表达式可以继续迭代
* @return
*/
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
PropertyNamer
可以根据 getter/setter
规范解析字段名称;PropertyCopier
则支持对有相同父类的对象,通过反射拷贝字段值。
2.7 MetaClass
MetaClass 通过 Reflector 和 PropertyTokenizer 组合使用, 实现了对复杂的属性表达式的解 析,并实现了获取指定属性描述信息的功能。
Reflector 实现了实体类元信息的封装,但是类中的成员变量是类的情况没有进行处理。
而 MetaClass 通过 ReflectorFactory 类型的成员变量,实现了实体类中成员变量是类情况的处理,通过与属性工具类的结合,实现了对复杂表达式的解析和实现了获取指定描述信息的功能。
其成员变量:
public class MetaClass {
//ReflectorFactory 对象,用于缓存 Reflector 对象
private ReflectorFactory reflectorFactory;
//在创建 MetaClass 时会指定一个类,该 Reflector 对象会用于记录该类相关的元信息
private Reflector reflector;
MetaClass 的构造函数中会为指定的 Class 创建相应的 Reflector 对象,井用其初始化 MetaClass.reflector 字段
MetaClass 构造函数是私有的,通过静态方法构建对象
//私有化构造函数
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;
}
/**
* 解析复杂的属性表达式,例如:<result property="orders[0].items[0].name"column="item1"/> 中 property="orders[0].items[0].name"
* 案例:
* @param name tele.name
* @param builder
* @return
*/
private StringBuilder buildProperty(String name, StringBuilder builder) {
//构造兼解析,name:tele.name
PropertyTokenizer prop = new PropertyTokenizer(name);//映射文件表达式迭代器
if (prop.hasNext()) {
//复杂表达式 //prop.getName():tele propertyName:tele
String propertyName = reflector.findPropertyName(prop.getName());
if (propertyName != null) {
builder.append(propertyName);
builder.append(".");
// 加载内嵌字段类型对应的MetaClass
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;
}
理解了这个方法(递归,该类中有很多类似的),就可以很好的对这个类进行理解,以查找(richType.richProperty)为例:
- 通过 PropertyTokenizer 对表达式进行解析, 得到当前的 name=richType, children=richProperty
- 从 reflector 中查找该 richType 属性
- 将 richType 添加到 builder 中
- 使用 metaClassForProperty 创建 richType 的 MetaClass
- 递归调用自身来处理子表达式
- 退出的条件就是没有子表达式。这个是为了,我们类中有成员变量是类,我们可以通过其找到他们的所有类及其属性。
注意,在此过程中,ReflectorFactory 一直是同一个,而其内部缓存了多个 Reflector 对象
2.8 ObjectWrapper
MetaClass 是 MyBatis 对类级别的元信息的封装和处理,下面来看 MyBatis 对对象级别的元 信息的处理。 ObjectWrapper 接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查 询对象属性信息的方法,以及更新属性的方法。
相对于 MetaClass
关注类信息,MetalObject
关注的是对象的信息,除了保存传入的对象本身,还会为对象指定一个 ObjectWrapper
将对象包装起来。ObejctWrapper
体系如下:
ObjectWrapper
的默认实现包括了对 Map
、Collection
和普通 JavaBean
的包装。MyBatis
还支持通过 ObjectWrapperFactory
接口对 ObejctWrapper
进行扩展,生成自定义的包装类。MetaObject
对对象的具体操作,就委托给真正的 ObjectWrapper
处理。
public interface ObjectWrapper {
//如果ObjectWrapper 中封装的是普通的 Bean 对象,则调用相应属性的相应 getter 方法,
//如采封装的是集合类,则获取指定 key 或下标对应的 value 位
Object get(PropertyTokenizer prop);
//如果 ObjectWrapper 中封装的是普通的 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/setter 方法
boolean hasSetter(String name);
boolean hasGetter(String name);
//为属性表达式指定的属性创建相应的 MetaObject 对象
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
boolean isCollection();
void add(Object element);
<E> void addAll(List<E> element);
}
BaseWrapper 是一个实现了 ObjectWrapper 接口的抽象类, 其中封装了 MetaObject 对象,并 提供了三个常用的方法供其子类使用,
BaseWrapper. resolveCollection()方法会调用 MetaObject. getValue()方法法,它会解析属性表达 式井获取指定的属性, MetaObject.getValue()方法的实现在后面详细介绍。
BaseWrapper.getCollectionValue()方法和 setCollectionValue()方法会解析属性表达式的索引 信息,然后获取/设置对应项。这两个方法的实现类似,这里只分析getCollectionValue ()方法
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {//如果是 Map 类型,如l index 为 key
return ((Map) collection).get(prop.getIndex());
} else {
int i = Integer.parseInt(prop.getIndex());//如果是其他集合类型,则 index 为下标
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[]) {
..........................
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
BeanWrapper 继承了 BaseWrapper 抽象类,其中封装了一个 JavaBean对象以及该 JavaBean 类相应的 MetaClass 对象,当然,还有从 BaseWrapper 继承下来的、该 JavaBean 对象相应的 MetaObject 对象。
例如赋值操作
@Override
public void set(PropertyTokenizer prop, Object value) {
if (prop.getIndex() != null) {
// 当前表达式是集合,如:items[0],就需要获取items集合对象
Object collection = resolveCollection(prop, object);
// 在集合的指定索引上赋值
setCollectionValue(prop, collection, value);
} else {
// 解析完成,通过Invoker接口做赋值操作
setBeanProperty(prop, object, value);
}
}
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
return object;
} else {
// 在对象信息中查到此字段对应的集合对象
return metaObject.getValue(prop.getName());
}
}
根据 PropertyTokenizer
对象解析出的当前字段是否存在 index
索引来判断字段是否为集合。如果当前字段对应集合,则需要在对象信息中查到此字段对应的集合对象:
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// 如果表达式仍可迭代,递归寻找字段对应的对象
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
// 字段解析完成
return objectWrapper.get(prop);
}
}
如果字段是简单类型,BeanWrapper
获取字段对应的对象逻辑如下:
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
// 集合类型,递归获取
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
// 解析完成,反射读取
return getBeanProperty(prop, object);
}
}
可以看到,仍然是会判断表达式是否迭代完成,如果未解析完字段会不断递归,直至找到对应的类型。前面说到 Reflector
创建过程中将对字段的读取和赋值操作通过 Invoke
接口抽象出来,针对最终获取的字段,此时就会调用 Invoke
接口对字段反射读取对象值:
/**
* 通过Invoker接口反射执行读取操作
*
* @param prop
* @param object
*/
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
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);
}
}
对象读取完毕再通过 setCollectionValue
方法对集合指定索引进行赋值或通过 setBeanProperty
方法对简单类型反射赋值。MapWrapper
的操作与 BeanWrapper
大致相同,CollectionWrapper
相对更会简单,只支持对原始集合对象进行添加操作。
2.9 MetaObject
ObjectWrapper 提供了获取/设置对象中指 定的属性值、检测 ge出r/setter 等常用功能,但是 ObjectWrapper 只是这些功能的最后一站,我 们省略了对属性表达式解析过程的介绍,而该解析过程是在 MetaObject 中实现的。
MetaObject中的属性如下:
//原始 JavaBean 对象
private final Object originalObject;
//ObjectWrapper 对象,其中封装了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;
// 根据传入object类型不同,指定不同的wrapper
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
// MetaObject 的构造方法是 private 修改的,只能通过 forObj ect ()这个静态方法创建 MetaObject 对象
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
三、小结
MyBatis
根据自身需求,对反射 API
做了近一步封装。其目的是简化反射操作,为对象字段的读取和赋值提供更好的性能。
org.apache.ibatis.reflection.Reflector
:缓存类的字段名和 getter/setter 方法的元信息,使得反射时有更好的性能。org.apache.ibatis.reflection.invoker.Invoker:
:用于抽象设置和读取字段值的操作。org.apache.ibatis.reflection.TypeParameterResolver
:针对 Java-Type 体系的多种实现,解析指定类中的字段、方法返回值或方法参数的类型。org.apache.ibatis.reflection.DefaultReflectorFactory
:默认的 Reflector 创建工厂。org.apache.ibatis.reflection.factory.ObjectFactory
:MyBatis 对象创建工厂,其默认实现 DefaultObjectFactory 通过构造器反射创建对象。org.apache.ibatis.reflection.property
:property 工具包,针对映射文件表达式进行解析和 Java 对象的反射赋值。org.apache.ibatis.reflection.MetaClass
:依赖 PropertyTokenizer 和 Reflector 查找表达式是否可以匹配 Java 对象中的字段,以及对应字段是否有 getter/setter 方法。org.apache.ibatis.reflection.MetaObject
:对原始对象进行封装,将对象操作委托给 ObjectWrapper 处理。org.apache.ibatis.reflection.wrapper.ObjectWrapper
:对象包装类,封装对象的读取和赋值等操作。
四、使用实例
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
//使用Reflector读取类元信息
Reflector findForClass = reflectorFactory.findForClass(TUser.class);
Constructor<?> defaultConstructor = findForClass.getDefaultConstructor();
String[] getablePropertyNames = findForClass.getGetablePropertyNames();
String[] setablePropertyNames = findForClass.getSetablePropertyNames();
System.out.println(defaultConstructor.getName());
System.out.println(Arrays.toString(getablePropertyNames));
System.out.println(Arrays.toString(setablePropertyNames));
//反射工具类初始化
ObjectFactory objectFactory = new DefaultObjectFactory();
TUser user = objectFactory.create(TUser.class);
ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
MetaObject metaObject = MetaObject.forObject(user, objectFactory, objectWrapperFactory, reflectorFactory);
ObjectWrapper wrapperForUser = new BeanWrapper(metaObject, user);
String[] getterNames = wrapperForUser.getGetterNames();
String[] setterNames = wrapperForUser.getSetterNames();
System.out.println(Arrays.toString(getterNames));
System.out.println(Arrays.toString(setterNames));
PropertyTokenizer prop = new PropertyTokenizer("userName");
wrapperForUser.set(prop, "lison");
System.out.println(user);
//模拟数据库行数据转化成对象
//1.模拟从数据库读取数据
Map<String, Object> dbResult = new HashMap<>();
dbResult.put("id", 1);
dbResult.put("user_name", "lison");
dbResult.put("real_name", "李晓宇");
TPosition tp = new TPosition();
tp.setId(1);
dbResult.put("position_id", tp);
//2.模拟映射关系
Map<String, String> mapper = new HashMap<String, String>();
mapper.put("id", "id");
mapper.put("userName", "user_name");
mapper.put("realName", "real_name");
mapper.put("position", "position_id");
//3.使用反射工具类将行数据转换成pojo
BeanWrapper objectWrapper = (BeanWrapper) metaObject.getObjectWrapper();
Set<Entry<String, String>> entrySet = mapper.entrySet();
for (Entry<String, String> colInfo : entrySet) {
String propName = colInfo.getKey();
Object propValue = dbResult.get(colInfo.getValue());
PropertyTokenizer proTokenizer = new PropertyTokenizer(propName);
objectWrapper.set(proTokenizer, propValue);
}
System.out.println(metaObject.getOriginalObject());
}