MyBatis
作为一款ORM
框架,主要通过XML
定义Object
,这就难免用到反射,虽然JDK
自带的反射已经方便使用,但是MyBatis
依然结合业务功能,将反射功能封装成一个更加易用的包,这个包就在reflection
中。
在解析MyBatis
的反色包之前,我们需要先通过需要看看MyBatis
需要通过反射实现哪些功能。
在MyBatis
中,可以方便的获取数组属性和对象属性。比如:student.names[0].firstName
因此,MyBatis
将反射获取值分为了两类:
- 容器类
POJO
类
容器类(Collection
)通过下标进行取值,而POJO
则是通过getter()/setter()
进行取值
一个POJO
对象拥有两类属性,对象属性以及类信息,在MyBatis
中分别通过通过MetaObject
和MetaClass
对应上述信息。
明白了上述信息,就能理解下面MyBatis
反射包中几个关键类的作用:
MetaClass
: 保存了POJO
的类相关信息,比如拥有的方法hasGetter()/hasSetter()
MetaObject
: 保存了POJO
对象的相关的信息,比如通过getter()
获取值,通过setter()
设置值ObjectWrapper
: 用来区别不同的POJO
获取属性的不同的方式,比如数组通过索引获取:nums[index]
,Map
通过key
获取,POJO
通过getter()
获取Relector
: 这个类便是MyBatis
的反射底层类,它简单的封装了JDK
底层的反射,其他类都是调用此类进行反射操作。
接下来开始分析MyBatis
的源码。
在relection
包中,包含有factory,invoker,property,wrapper
4个包,
factory
: 工厂类,主要用来通过反射创建类invoker
: 对POJO
的各种类的封装以及缓存,方便直接调用property
: 设置/获取POJO
属性的帮助类wrapper
:POJO
的包装类,只要需要操作POJO
的属性,即可通过此类包装后调用
在MyBatis
中,主要通过reflection.SystemMetaObject
作为简单工厂使用:
public final class SystemMetaObject {
// 默认创建POJO的工厂类
public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
// 创建BeanWrapper的工厂类
public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
// null 对应的Object包装类
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
}
private static class NullObject {}
//简单工厂方法 用来创建MetaObject
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
}
}
可以看到,这里是直接返回的MetaObject
。
MetaObject
MetaObject
主要有如下方法:
getSetterNames()
: 获取该class
所有的setter name
getGetterNames()
: 获取该class
所有的getter name
hasGetter()
: 此class
是否有该属性的getter()
hasSetter()
: 此class
是否有该属性的setter()
getValue()
: 获取对应属性对应的值setValue()
: 设置此属性为对应的值
从这里可以看出来,MetaObject
基本包含了MyBatis
需要使用的所有功能,可以说通过此类,基本连接了XML
与POJO
。
在MetaObject
代码中,基本上都是调用的objectWrapper
的方法,而MetaObject
主要作用在于根据类型创建对应的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;
}
//如果wrapper工厂能够包装此`object`,则直接交给工厂处理
else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
}
//如果对象是Map 则直接使用`MapWrapper`处理
else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
}
//如果对象属于collection,则使用`CollectionWrapper`处理
else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
}
//否则,使用`BeanWrapper`包装此对象
else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
这里有个不合理的地方,查看
CollectionWrapper
的源码可以看到,CollectionWrapper
中全是抛出异常。也就是如果传入的是List
之类的对象,调用任何方法都会抛出异常。结合
MyBatis
整个源码可以发现,MyBatis
会处理List
,Array
等,然后将其包装到StrictMap
中:private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap<Object> map = new StrictMap<>(); map.put("collection", object); if (object instanceof List) { map.put("list", object); } return map; } else if (object != null && object.getClass().isArray()) { StrictMap<Object> map = new StrictMap<>(); map.put("array", object); return map; } return object; }
而
Map
中会有逻辑处理Collection
在MetaObject
中还有一个重要逻辑,前面说过,可能会解析类似student.names[0].firstName
的属性,这样就涉及到一个循环处理,并且第一个是POJO
,第二个是集合。
在MyBatis
中,通过递归调用来循环解决:
public Object getValue(String name) {
//创建属性解析
PropertyTokenizer prop = new PropertyTokenizer(name);
//如果属性中包含需要解决的子属性
if (prop.hasNext()) {
//再次创建 `MetaObject`
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
//递归调用`MetaObject#getValue()
return metaValue.getValue(prop.getChildren());
}
} else {
//通过`ObjectWrapper`获取属性
return objectWrapper.get(prop);
}
}
其实这样写倒比较难看懂,也不知道为什么不直接使用
foreach
,而且PropertyTokenizer
也实现了Iterator
接口
getValue
之所以要放在这一层,便是因为能处理student.names[0].firstName
,每一层递归都会新建一个MetaObject
对象,然后根据子对象的类型再次通过对应类型的ObjectWrapper
去获取值,直到获取到最终类型的值。
看完了MetaObject
,接下来看MetaObject
中所包装的ObjectWrapper
ObjectWrapper
在MyBatis
的实现主要分两类:BeanWrapper
和MapWrapper
其中这两个Wrapper
都继承自BaseWrapper
,BaseWrapper
中包含了一些通用的方法:
resolveCollection()
: 通过key
或者getter()
获取collection
getCollectionValue()
: 通过下标,获取数组或容器的指定下标的值setCollectionValue()
: 通过下标,设置数组或者容器的值
ObjectWrapper
ObjectWrapper
接口所拥有的方法和MetaObject
的方法差不多,只不过其实现类分几种。
MapWrapper#get()
@Override
public Object get(PropertyTokenizer prop) {
//判断表达式中是否包含类似list[1]符号
if (prop.getIndex() != null) {
//如果有,则先从Map中获取此集合
Object collection = resolveCollection(prop, map);
//从集合中获取指定下标的元素
return getCollectionValue(prop, collection);
} else {
//否则,通过使用map的key获取参数
return map.get(prop.getName());
}
}
BeanWrapper#get()
@Override
public Object get(PropertyTokenizer prop) {
//判断表达式中是否包含类似list[1]符号
if (prop.getIndex() != null) {
//如果有,则先从现在的POJO中获取此集合
Object collection = resolveCollection(prop, object);
return getCollectionValue(prop, collection);
} else {
//否则,通过`getter`获取属性的值
return getBeanProperty(prop, object);
}
}
接下来看通过getBeanProperty()
获取属性的具体方法:
BeanWrapper#getBeanProperty()
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
//获取对应方法的Invoker
Invoker method = metaClass.getGetInvoker(prop.getName());
//通过Invoker调用对应的方法
return method.invoke(object, NO_ARGUMENTS);
}
可以看到,BeanWrapper
底层有一部分是通过MetaClass
获取的信息
接下来看MetaCalss
的方法:MetaClass
可以类比Java
中的Class
,主要保存了Class
的各种信息,按道理来说应该做一个缓存将MetaClass
缓存起来,但是在MyBatis
中缓存的是Refector
,差别不大、
MetaClass
MetaClass
主要包含以下方法:
findProperty()
: 查找属性,主要用于查找映射数据库的字段和POJO
的属性getSetterType()
: 获取对应setter
需要的字段类型getGetterType()
: 获取对应getter
返回的字段类型hasSetter()
: 是否存在对应属性的setter
hasGetter()
: 是否存在对应属性的getter
get*Invoker()
: 获取对应方法的Invoker
,获取后可以直接调用
可以看到,都是涉及到class
的一些属性。
而MetaClass
主要功能为分析PropertyTokenizer
然后底层调用Reflector
。
比如:
public boolean hasSetter(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
//判断是否有子节点需要解析
if (prop.hasNext()) {
//首先判断本身是否有getter,只有本身存在getter,才能获取到子节点
if (reflector.hasSetter(prop.getName())) {
//创建子节点`MetaClass`
MetaClass metaProp = metaClassForProperty(prop.getName());
//递归处理子节点
return metaProp.hasSetter(prop.getChildren());
} else {
return false;
}
} else {
//通过reflector获取setter
return reflector.hasSetter(prop.getName());
}
}
Reflector
Reflector
主要通过简单工厂创建:
DefaultReflectorFactory#findForClass()
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
主要是为了能够缓存已经解析的信息
Reflector
主要成员变量如下:
//类型信息
private final Class<?> type;
//可读的属性(有getter)
private final String[] readablePropertyNames;
//可写的属性(有setter)
private final String[] writablePropertyNames;
//set对应的Invoker
private final Map<String, Invoker> setMethods = new HashMap<>();
//get对应的Invoker
private final Map<String, Invoker> getMethods = new HashMap<>();
//属性setter需要的类型
private final Map<String, Class<?>> setTypes = new HashMap<>();
//属性getter返回的类型
private final Map<String, Class<?>> getTypes = new HashMap<>();
//默认构造方法
private Constructor<?> defaultConstructor;
对于Reflector
,主要便是通过反射处理对应的Class
,然后将获取的属性填充到上面的成员变量中。方法不一一分析,这里举例分析一个:
Reflector#getClassMethods()
private Method[] getClassMethods(Class<?> clazz) {
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
//循环处理当前类,然后处理其父类,不处理`Object`
while (currentClass != null && currentClass != Object.class) {
//首先将当前类的所有方法加入到容器中
//方法中主要为Method生成一个唯一的签名
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[0]);
}
这个方法是比较简单的方法,但是Reflector
的逻辑远远不是这么简单,这其中会涉及到很多细节的语法处理,比如在子类重写父类方法中,返回类型可能不同,这个时候应该以子类的为准等等。。
到这里,MyBatis
的反射类基本分析完毕,其实看起来这么复杂,但是只要理解了MyBatis
对反射的需求,看起来就不难明白了。
在整个解析过程中,起着最大的作用的类是:PropertyTokenizer
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
//以下属性以`student.names[0].firstName` 为例
//student
private String name;
//names
private final String indexedName;
//0
private String index;
//names
private final String children;
public PropertyTokenizer(String fullname) {
//通过点判断是否有子表达式
int delim = fullname.indexOf('.');
if (delim > -1) {
//当前表达式
name = fullname.substring(0, delim);
//找到子表达式
children = fullname.substring(delim + 1);
} else {
name = fullname;
children = null;
}
indexedName = name;
//通过[找到索引取值表达式
delim = name.indexOf('[');
if (delim > -1) {
index = name.substring(delim + 1, name.length() - 1);
name = name.substring(0, delim);
}
}
}
这个类就是分析student.names[0].firstName
这个取值的关键类,并且这个类实现了Iterator
接口,因此可以直接使用foreach
便利,但是MyBatis
的代码并没有这样做,而是使用了递归。
总结
这里再次复习一下,MyBatis
的反射使用入口是SystemMetaObject
,它包含了整个反射包的基本配置。
在MyBatis
的代码中,很少能看到静态类,静态工具方法等,基本都是通过对象“注入”到对应的其他对象中。这样当需要修改实现的时候,就能很好的维护。
在MyBatis
中,首先使用MetaObject
,MetaObject
内部充当了一个简单工厂方法,用来分辨是处理Map
还是处理List
还是Bean
,
ObjectWrapper
根据不同的实现,使用不同的方式获取底层的值,对于Bean
来说,使用的是MetaClass
,
MetaClass
包含了对应Bean
的Class
的各种元属性。其中MetaClass
底层使用的Reflector
对象
Reflector
对象便是真正的反射实现者,其内部根据传入的Class
,生成了各种信息,并且由于Class
是通用的,因此MyBatis
使用ReflectorFactory
将其缓存起来。