文章目录
前言
MyBatis作为一个ORM框架,除了将对象存储到数据库(对象属性与数据库字段的映射),同样还要考虑将数据库字段映射为对象的属性。比如一个非常简单的查询语句
City findByState(@Param("state") String state);
<select id="findByState" resultType="sample.mybatis.domain.City" flushCache="true">
select *
from city
where state = #{state}
</select>
在这里就需要先创建一个City对象,然后将对应的值赋值到这个对象当中。对于像MyBatis这样的框架,会考虑通过new去创建这个对象吗?首先通过new去创建必须当前classpath下存在这个类,而MyBaits中不会存在这样的实体类,第二个实际场景中,这样的类千千万万,通过new来创建也是不可能完成的事情,所以,只能是通过反射来创建。如果是一个简单的映射关系还好,MyBatis可能面对的情况比这个复杂多了。比如,如果要返回一个List<City>,那么该如何反射呢?当然了,MyBatis出色的完成了这些任务,这都是因为MyBatis超强大的反射模块。本章我们来学习一下MyBatis的反射模块。
MyBatis反射模块
MyBatis反射模块位于org.apache.ibatis.reflection
包内,结构如下
其中比较关键的几个类如下:
1. ObjectFactory
:MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构件POJO
2. ReflectorFactory
:创建Reflector的工厂类,Reflector是MyBatis反射模块的基础,每个Reflector对象都对应一个类,在其中缓存了反射操作所需要的类元信息。
3. ObjectWrapper
:对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息的方法,以及更新属性的方法
4. ObjectWrapperFactory
:ObjectWrapper的工厂类,用于创建ObjectWrapper
5. MetaObject
:封装了对象元信息,包装了MyBaits中的五个核心的反射类。也是提供给外部使用的反射工具类,可以利用它读取或者修改对象的属性信息,可以使用org.apache.ibatis.reflection.SystemMetaObject#forObject创建。
它们之间的关系图如下所示
SystemMetaObject使用说明
如果想对MyBatis的反射模块有个快速了解的方式,我们先得有一个好的入口。通过这个入口可以将这个模块的各个类联系起来,而这个入口就非org.apache.ibatis.reflection.SystemMetaObject
莫属了。可以看一下这个类的实现
public final class SystemMetaObject {
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
private SystemMetaObject() {
// Prevent Instantiation of Static Class
}
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
}
private static class NullObject {
}
}
代码不多,却将我们上面所说的几个关键类都使用到了。我们先从这个类的使用方法出手。创建一个用于测试的类
package org.apache.ibatis.domain.misc;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RichType {
private RichType richType;
/**
* 这个属性不包含setter和getter方法 但是MyBatis仍然能够设置值 这样可以解决一些命名不规范的情况
*/
private String richField;
private String richProperty;
private Map richMap = new HashMap();
private List richList = new ArrayList() {
{
add("bar");
}
};
public RichType getRichType() {
return richType;
}
public void setRichType(RichType richType) {
this.richType = richType;
}
public String getRichProperty() {
return richProperty;
}
public void setRichProperty(String richProperty) {
this.richProperty = richProperty;
}
public List getRichList() {
return richList;
}
public void setRichList(List richList) {
this.richList = richList;
}
public Map getRichMap() {
return richMap;
}
public void setRichMap(Map richMap) {
this.richMap = richMap;
}
}
现在我们创建这个对象,并通过MyBatis的反射模块为这个对象设置值和获取值,如下所示
@Test
public void shouldGetAndSetField() {
RichType rich = new RichType();
// 获取元数据类型对象
MetaObject meta = SystemMetaObject.forObject(rich);
// 设置目标属性值
meta.setValue("richField", "foo");
// 获取目标属性值
assertEquals("foo", meta.getValue("richField"));
}
这里分为如下几部
1. 通过SystemMetaObject获取一个元数据对象
2. 通过元数据对象的setValue方法设置richField属性的值为foo
3. 通过元数据对象的getValue方法获取richField的值并验证与foo相同。证明上面的setValue方法有效。
下面就通过以上三步来了解MyBatis的反射模块
1. 获取元数据对象
org.apache.ibatis.reflection.SystemMetaObject#forObject是SystemMetaObject的唯一的一个方法,实际会调用org.apache.ibatis.reflection.MetaObject#forObject的方法,只不过在SystemMetaObject当中使用了ObjectFactory、ObjectWrapperFactory、ReflectorFactory这些接口的默认实现,屏蔽了复杂性。
public static MetaObject forObject(Object object, ObjectFactory objectFactory,
ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
1. 当前对象不为空 所以会使用MetaObject的构造方法创建一个对应的元数据对象
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
MetaObject构造类如下,
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory,
ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
1. 本身就是ObjectWrapper 不需要再包装了
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
2. 判断工厂里是不是存在已经创建好的对象 目前实现一直返回false 应该缓存目的 但未实现
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
3. 根据数据的不同结构 进行不同的封装 封装的时候 将自己传入
} 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);
}
}
在这里会保存原来的对象originalObject,并根据对象类型构造不同的ObjectWrapper
。比如如果是Map对象就对应MapWrapper,Collection对象就对应CollectionWrapper,而其他对象对应的BeanWrapper。
这里是一个普通对象,所以创建org.apache.ibatis.reflection.wrapper.BeanWrapper#BeanWrapper对象,对应的构造如下
public BeanWrapper(MetaObject metaObject, Object object) {
1. 将元数据保存到包装类当中
super(metaObject);
2. 设置真实独享
this.object = object;
3. 创建元类数据 这个对象中包含了一个类的各种方法信息 属性信息
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
如果一个对象对应一个Class信息,而metaObject这里也对应一个MetaClass,对应的方法为org.apache.ibatis.reflection.MetaClass#forClass
。
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
1. 调用到真实构造
return new MetaClass(type, reflectorFactory);
}
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
2. 通过类型获取Reflector对象 这个对象是MyBatis反射模块的核心类
this.reflector = reflectorFactory.findForClass(type);
}
通过org.apache.ibatis.reflection.DefaultReflectorFactory创建Reflector对象
private boolean classCacheEnabled = true;
/**
* 保存类与反射包装对象的缓存 关于性能的考虑
*/
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();
@Override
public Reflector findForClass(Class<?> type) {
1. 使用缓存
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type); // 缓存反射对象
if (cached == null) {
2. 无法从缓存获取 创建一个新的对象
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
通过以上的各种包装,最终要进去核心了,我们看一下Reflector这个类,通过这个类包装了一个指定类(type)的默认构造、所有的属性以及setter/getter方法参数类型、方法反射调用(Invoker对象)。
属性名称、属性类型这个都容易理解,而Invoker是啥呢?直接通过对应的setter/getter方法的反射调用不就好了吗?确实,MyBatis也是打算这么做的,比如在org.apache.ibatis.reflection.invoker.MethodInvoker中实现如下
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, args);
}
可以看到这里其实就是对应的方法反射,但是在有些情况下,用户没有生成setter/getter方法,又或者setter/getter方法不规范。那么此时该怎么办呢?MyBatis处理方式也比较简单,直接找到对应的属性的反射类Field对象,调用Filed对象的set和get方法也可以将值设置到对应属性当中或者从属性当中获取目标值。对应的另外两个Invoker方法的实现如下
org.apache.ibatis.reflection.invoker.GetFieldInvoker
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return field.get(target);
}
org.apache.ibatis.reflection.invoker.SetFieldInvoker
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
field.set(target, args[0]);
return null;
}
在Reflector当中建立属性名称和Invoker的对应关系,就是为了通过后续针对指定的属性名称设置属性值,对应的构造方法如下。
public Reflector(Class<?> clazz) {
type = clazz;
1. 设置默认构造器
addDefaultConstructor(clazz);
2. 解析getter方法
addGetMethods(clazz);
3. 解析setter方法
addSetMethods(clazz);
4. 添加属性与FieldInvoker的映射关系
addFields(clazz);
5. 设置getter属性名称
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
6. 设置setter属性名称
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
7. 保存属性名称的大写 方便通过属性大写设置值
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
- 解析默认构造方法比较简单,就是查找是不是存在参数个数为0而且是public公开方法的构造器
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访问级别的),方法参数个数为0,方法名称包含get或者is。对应的方法不是首先加入到getMethods这个映射关系中,因为可能一个属性会对应多个getter方法,因此先保存到一个属性与方法列表的映射中,最后如果存在多个方法,则会根据一定的算法解决冲突选取一个winner添加到getMethods映射中。
private void addGetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
1. 获取所有的方法 包括父类 接口 的 public 设置 private
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
2. getter方法的参数个数为0
if (method.getParameterTypes().length > 0) {
continue;
}
3. 获取方法名称 只有开头为get或者is的才是getter方法
String name = method.getName();
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
4. 解析参数名称 并将首字母变大写 比如getRichMap->richMap
name = PropertyNamer.methodToProperty(name);
5. 将方法名称和方法添加到conflictingGetters中 数据结构为属性与对应方法列表的映射
addMethodConflict(conflictingGetters, name, method);
}
}
6. 如果一个属性对一个多个名称 解决冲突 最后填充getMethods和getTypes属性
resolveGetterConflicts(conflictingGetters);
}
解决冲突的算法:首先将第一个作为winner,后续每个与第一个进行PK。 比较二者的返回类型,如果是一致的,比如说同时存在is开头的和get开头的,会以is获胜,但这种情况只适合返回类型为boolean,否则会抛出异常。如果类型不一致,要存在父子或者实现关系,类型小的获胜,为什么呢?子类方法类型要比父类小,不存在父子或实现关系,会抛出ReflectionException异常。
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()) {
1. 第一个作为winner
if (winner == null) {
// 将第一个作为winner
winner = candidate;
continue;
}
2. 进行类型PK
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
if (candidateType.equals(winnerType)) {
2.1 类型一致 返回类型必须是boolean 这个时候is方法获胜
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)) {
2.2 类型不一致 存在父子或实现关系 子获胜
winner = candidate;
} else {
2.3 类型不一致 且不存在父子或实现关系 抛出异常
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);
}
}
解决冲突之后,保存映射关系,这段代码看起来蛮简单,其实resolveReturnType和typeToClass方法都是极其复杂的。因为对于Class来说,大家比较熟悉,但是java.lang.reflect.Type的了解就未必那么深了。在这里MyBatis为我们封装了一个工具类TypeParameterResolver,屏蔽了各种泛型类型的复杂性。这里不会对Type以及这个工具方法深入探讨,感兴趣的可以查阅相关资料、博客或者参考本人的博客:Java基础篇之Type学习总结
private void addGetMethod(String name, Method method) {
1. 判断是不是合格的属性名称 以$开头、名称为serialVersionUID或者class都是无效的
if (isValidPropertyName(name)) {
2. 保存属性名称为Invoker对象的映射 Invoker对象仅仅是对对应方法的封装
getMethods.put(name, new MethodInvoker(method));
3. 通过TypeParameterResolver工具类解析返回参数类型
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
4. 保存属性名称和对应方法参数类型的映射
getTypes.put(name, typeToClass(returnType));
}
}
Type转Class
private Class<?> typeToClass(Type src) {
Class<?> result = null;
if (src instanceof Class) {
1. 本来就是Class类型的
result = (Class<?>) src;
} else if (src instanceof ParameterizedType) {
2. ParameterizedType类型的,返回原始类型 比如List<String> 返回java.util.List
result = (Class<?>) ((ParameterizedType) src).getRawType();
} else if (src instanceof GenericArrayType) {
3. 泛型数组类型 获取基础类型
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();
}
}
4. 不属于以上类型或者无法获取到值 则取Object
if (result == null) {
result = Object.class;
}
return result;
}
- 解析setter方法,大体流程和上面是一样的,除了方法名称与参数个数要求不一样之外,没其他区别
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();
1. 与addGetMethods流程是一致的 除了setter方法名称是以set开头 而且参数个数为1
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingSetters, name, method);
}
}
}
resolveSetterConflicts(conflictingSetters);
}
- 如果通过以上两步一个属性还不包含对应的Invoker方法(setter和getter)都必须,则通过包装属性为Invoker对象了。
private void addFields(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
1. 遍历所有属性
for (Field field : fields) {
if (canAccessPrivateMethods()) {
try {
field.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
if (field.isAccessible()) {
2. 不包含属性对应setMethod则创建一个SetFieldInvoker 但是要排除那些final static的属性 因为这种类型的属性不可以通过反射设置值
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);
}
}
3. 不包含属性对应getMethod则创建一个GetFieldInvoker
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
}
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
对应的添加方法如下,与之前的基本相似,就是构造的Invoker对象不同。
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));
}
}
- 通过以上的处理之后,基本的工作已经完成了。接下来无非就是保存一下getMethods和setMethods这两个Map中的key(类属性)到可写属性数组writeablePropertyNames和可读属性数组readablePropertyNames中了。为了兼容属性大写的情况,还保存了另外两个Mapper映射caseInsensitivePropertyMap和caseInsensitivePropertyMap。最后构造的Reflector对象如下
完成Reflector对象的创建,就完成了MetaClass、BeanWrapper、MetaObject对象的创建。
2. 通过MetaObject对象设置属性值
对应的调用方法meta.setValue("richField", "foo");
,按照道理来说,现在在反射对象当中包含了richField属性对应的SetInvoker方法,直接调用就可以了,但是看一下具体实现,发现会比想象中复杂。为啥呢?其实MyBatis面对的场景比较复杂,除了以上的方式,还有
1 针对属性的属性值的操作
meta.setValue("richType.richField", "foo");
assertEquals("foo", meta.getValue("richType.richField"));
2. 针对于Map类型的属性值添加元素方式1
meta.setValue("richMap.key", "foo");
assertEquals("foo", meta.getValue("richMap.key"));
3. 针对于Map类型的属性值添加元素方式2
meta.setValue("richMap[key]", "foo");
assertEquals("foo", meta.getValue("richMap[key]"));
4. 针对于属性值的Map类型的属性值添加元素方式1
meta.setValue("richType.richMap.key", "foo");
assertEquals("foo", meta.getValue("richType.richMap.key"));
5. 针对于List类型的属性值添加元素方式
meta.setValue("richList[0]", "foo");
assertEquals("foo", meta.getValue("richList[0]"));
所以这里属性的类型是多样的,而且还包括属性的属性,多层次的解析情况。所以在这里使用了PropertyTokenizer这个类,主要用于记录当前层次的属性名称和下一个层级的属性名称以及如果是列表数组之类的,通过index记录对应的索引。
public void setValue(String name, Object value) {
1. 构造一个PropertyTokenizer记录要设置的属性名称
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
2. 包含有下一个层级 首先获取这个层级属性对应的值 构造MetaObject对象 与我们上面步骤1是一样的
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null && prop.getChildren() != null) {
// don't instantiate child path if value is null
return;
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
// 设置子属性的值 所谓的子属性 就是对应类的属性
metaValue.setValue(prop.getChildren(), value);
} else {
3. 不包含下一个层级关系
objectWrapper.set(prop, value);
}
}
不包含多层级关系的话,调用org.apache.ibatis.reflection.wrapper.BeanWrapper#set方法
@Override
public void set(PropertyTokenizer prop, Object value) {
if (prop.getIndex() != null) {
1.1. 包含有索引 那么首先获取对应的Collection对象
Object collection = resolveCollection(prop, object);
2. 设置集合类型的值
setCollectionValue(prop, collection, value);
} else {
1.2. 普通类型的处理方式
setBeanProperty(prop, object, value);
}
}
首先看一下普通方式,这里比较简单,就是从metaClass里面的reflector中根据属性名称获取到目标方法,然后调用目标方法即可。
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
try {
1. 从reflector中获取到属性名称对应的反射调用方法
Invoker method = metaClass.getSetInvoker(prop.getName());
2. 设置反射的参数值 因为是setter 所以参数个数为1
Object[] params = {value};
try {
3. 反射调用
method.invoke(object, params);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (Throwable t) {
throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass()
+ "' with value '" + value + "' Cause: " + t.toString(), t);
}
}
如果属性是一个列表的话。可以看到此时prop中包含了index属性。
通过resolveCollection方法获取到对应属性名称(这里为richList)的值。
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
return object;
} else {
return metaObject.getValue(prop.getName());
}
}
最后无非就是通过MetaObject对象获取属性值,这一块将在下一部分介绍。
获取了对应的属性值之后,就是设置集合指定索引的属性值了。
protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
if (collection instanceof Map) {
((Map) collection).put(prop.getIndex(), value);
} else {
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
((List) collection).set(i, value);
} else if (collection instanceof Object[]) {
((Object[]) collection)[i] = value;
} else if (collection instanceof char[]) {
((char[]) collection)[i] = (Character) value;
} else if (collection instanceof boolean[]) {
((boolean[]) collection)[i] = (Boolean) value;
} else if (collection instanceof byte[]) {
((byte[]) collection)[i] = (Byte) value;
} else if (collection instanceof double[]) {
((double[]) collection)[i] = (Double) value;
} else if (collection instanceof float[]) {
((float[]) collection)[i] = (Float) value;
} else if (collection instanceof int[]) {
((int[]) collection)[i] = (Integer) value;
} else if (collection instanceof long[]) {
((long[]) collection)[i] = (Long) value;
} else if (collection instanceof short[]) {
((short[]) collection)[i] = (Short) value;
} else {
throw new ReflectionException(
"The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
可以看出来,这里并没有什么特别的,无非就是转换类型和设置值,只不过要考虑的类型比较多而已。
如果调用方式为meta.setValue(“richType.richMap[key]”, “foo”),此时就是设置richType属性对应的值里面对应的richMap的值了。这是就存在层级关系了。children包含有值。
这时需要首先获取到richType这个属性所对应的值并构造元数据对象。如果此时这个属性还没有设置值怎么办?通过forObject会返回一个SystemMetaObject.NULL_META_OBJECT对象。一个对NullObject对象的包装。这里并不会报错。然后调用org.apache.ibatis.reflection.wrapper.ObjectWrapper#instantiatePropertyValue创建一个空的对象。
/**
* 初始化属性值
*
* @param name 比如richType.richMap[key]
* @param prop
* @param objectFactory
* @return 返回属性对应的元数据对象
*/
@Override
public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
MetaObject metaValue;
1. 获取richType属性对应的setter方法的返回类型 最终是会调用到Reflector中的方法
Class<?> type = getSetterType(prop.getName());
try {
2. 通过objectFactory创建一个空的对象
Object newObject = objectFactory.create(type);
3. 再根据newObject创建MetaObject
metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(),
metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
4. 反射设置值
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;
}
以上的这个流程看起来其实是简单的,但真实情况远比这表面复杂多了,比如就通过objectFactory创建指定类型type的实例来说。比如说如果type是List或Map该怎么办?这只是接口呀,怎么创建对象呢?其实MyBatis都是采用了我们常用的实现类而已。对应的方法为org.apache.ibatis.reflection.factory.DefaultObjectFactory#resolveInterface
。
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;
}
MyBatis通过DefaultObjectFactory工厂类为我们屏蔽了复杂性。另外我们上面的instantiatePropertyValue方法是BeanWrapper
的,还有MapWrapper和CollectionWrapper呢?前者创建一个HashMap对象。(如果调用的方式为meta.setValue(“richMap.key”, “foo”),而且RichType这个类中的richMap的初始值为null,此时就会调用到这里的)
@Override
public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
HashMap<String, Object> map = new HashMap<String, Object>();
set(prop, map);
return MetaObject.forObject(map, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(),
metaObject.getReflectorFactory());
}
后者直接抛出一个异常,因为对于Collection类型的数据不会涉及到子层级关系,只有索引关系,在上面已经提到过。
@Override
public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
throw new UnsupportedOperationException();
}
总之,这里要把多种类型都考虑进来,是真的不简单。通过以上方式构造了另一个层次的MetaObject,其结果如下
接下来通过org.apache.ibatis.reflection.MetaObject#setValue设置属性,还是调用相同的方法(类似递归),只不过是不同的MetaObject对象而已。
总结一下:通过org.apache.ibatis.reflection.MetaObject#setValue设置属性值如果是一个层级的属性,不涉及Collection、Map这些复杂类,其实就是通过属性获取反射方法调用一下就好了,但是设及到Collection的时候,就需要先获取或者创建对应的Collection对象,然后根据索引设置值,尤其是涉及到多个层次的属性设置,那就要递归了。这些复杂关系MyBatis通过org.apache.ibatis.reflection.property.PropertyTokenizer这个数据结构来处理的。体现了数据结构的重要性。
3. 通过MetaObject对象获取属性值
通过以上设置值的过程,我们应该对获取属性值有一个难度的预期了。其实获取值要简单一点,因为设置值如果不存在就需要先去创建一个,而获取值就不一样了,不会涉及创建属性的过程。
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
1. 如果存在子层级 则首先获取第一层级的值
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
2. 类似于递归调用 同一个方法 不同对象
return metaValue.getValue(prop.getChildren());
}
} else {
3. 不存在层级关系
return objectWrapper.get(prop);
}
}
如果是BeanWrapper对象的话
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
1. 考虑存在索引的问题
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
2. 普通类型
return getBeanProperty(prop, object);
}
}
- 普通不带索引类型,比较简单,比如meta.getValue(“richField”)。
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
1. 根据属性名称获取reflector中的Invoker对象
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
2. 调用Invoker对象的invoke方法 getter方法不需要参数
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);
}
}
org.apache.ibatis.reflection.Reflector#getGetInvoker很简单,无非hash计算
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;
}
- 如果包含有索引 比如 meta.getValue(“richList[0]”)
首先是获取richList的值,与上面获取普通值是一样的
获取列表的值之后,再根据索引读取值啦。
继续获取,如果是MapWrapper对象的话。
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
1. 考虑存在索引的问题 比如meta.getValue("richMap[key]")
Object collection = resolveCollection(prop, map);
return getCollectionValue(prop, collection);
} else {
2. 普通类型 meta.getValue("richType.richMap.key") 其实这里是层级关系 但层级关系在前面已经处理了
return map.get(prop.getName());
}
}
总结一下:通过org.apache.ibatis.reflection.MetaObject#getValue获取值其实要比setValue要简单一点,因为此时不涉及到setValue,在setValue当中会涉及到不存在属性需要创建一个默认属性获取MetaObject的过程,getValue就是一层一层获取值的过程。在MetaObject在get方法中解决层级关系的问题,然后在ObjectWrapper的get方法中解决索引问题或者最终反射调用获取真实值
总结
MyBatis反射模块是极其强大的,但使用起来确实非常的简单,通过org.apache.ibatis.reflection.SystemMetaObject简单的几行代码就能完成非常复杂的操作。同样在MyBatis反射模块中我们也可以学习到不少有用的反射知识。回到前言中的一个问题,MyBatis是如何反射一个List<City>对象的呢?其实在org.apache.ibatis.reflection.Reflector#typeToClass当中返回的是java.util.List,在org.apache.ibatis.reflection.factory.DefaultObjectFactory#create方法中其实创建的是一个简单的java.util.ArrayList对象,只不过最后将一个原始类型的List赋值给了泛型List而已。可以参考源码org.apache.ibatis.executor.result.DefaultResultHandler。