2.9.解析 ObjectWrapperFactory 元素 解析并配置对象包装工厂
在前面的文章中,我们接触过很多个工厂对象,比如:SqlSessionFactory
、ObjectFactory
和ReflectoryFactory
。
ObjectWrapperFactory
也是一个工厂对象,他用于将普通的JAVA对象包装成ObjectWrapper
对象。
在这里我将ObjectWrapper
叫做对象包装器,因为ObjectWrapper
对象的作用就是包装对象后为其提供统一的属性操作方法。
鉴于ObjectWrapperFactory
就是一个创建ObjectWrapper
实例的接口定义,因此,我称之为对象包装器工厂。
ObjectWrapperFactory
接口中定义了两个方法:
/**
* 一个对象包装器创建工厂的接口定义,他的实现类负责将普通的java对象包装成ObjectWrapper实例。
* @author Clinton Begin
* @see org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue(ResultSetWrapper, ResultMap, String)
*/
public interface ObjectWrapperFactory {
/**
* 是否拥有指定对象的装饰对象
* @param object 指定该对象
*/
boolean hasWrapperFor(Object object);
/**
* 通过对象元数据获取指定对象的包装对象
* @param metaObject 对象元数据
* @param object 指定对象
*/
ObjectWrapper getWrapperFor(MetaObject metaObject, Object object);
}
其中,hasWrapperFor
方法用于判断当前的对象包装工厂是否可以为指定的对象创建对象包装器,getWrapperFor
方法则用于实际获取指定对象的对象包装器——一个ObjectWrapper
实例。
根据方法的定义来看,可以想象该接口的方法调用大致会是如下格式:
// 通过hasWrapperFor校验,通过getWrapperFor进行转换
if (objectWrapperFactory.hasWrapperFor(object)) {
// 通过包装器工厂进行包装
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
}
ObjectWrapperFactory
接口的默认实现类是DefaultObjectWrapperFactory
,被硬编码在Configuration
对象中:
/**
* 配置对象包装器工厂,主要用于将java对象包装成{@link org.apache.ibatis.reflection.wrapper.ObjectWrapper}
*/
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
DefaultObjectWrapperFactory
的实现比较简单,他的hasWrapperFor
方法总是返回false
,这就意味着他不会将任何对象包装成ObjectWrapper
实例,所以他的getWrapperFor
方法理论上也不会被调用:
/**
* 默认的对象包装器工厂
*
* @author Clinton Begin
*/
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {
/**
* 是否拥有指定对象的装饰对象
*
* @param object 指定该对象
*/
@Override
public boolean hasWrapperFor(Object object) {
// 恒定false
return false;
}
/**
* 通过对象元数据获取指定对象的包装对象
*
* @param metaObject 对象元数据
* @param object 指定对象
*/
@Override
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
}
}
ObjectWrapperFactory
的getWrapperFor
方法有一个MetaObject
类型的参数,该参数的作用和我们之前了解过的MetaClass
对象有所类似, 不同于MetaClass
用于保存类型元数据,MetaObject
用于保存指定对象的元数据并提供操作对象元数据的方法。
换句话来说MetaClass
是类型描述对象,MetaObject
则是对象描述对象。
作为对象描述对象的MetaObject
类中定义了五个属性,这五个属性的存在的目的是封装被描述对象,简化其属性和方法的访问和操作。
/**
* 被包装的原始对象
*/
private final Object originalObject;
/**
* 原始对象的包装器
*/
private final ObjectWrapper objectWrapper;
/**
* 实例化对象的工厂
*/
private final ObjectFactory objectFactory;
/**
* 创建对象包装器的工厂
*/
private final ObjectWrapperFactory objectWrapperFactory;
/**
* 创建Reflector对象的反射工厂
*/
private final ReflectorFactory reflectorFactory;
这些属性的初始化操作是在MetaObject
的构造方法中完成的:
/**
* @param object 原始对象
* @param objectFactory 对象工厂
* @param objectWrapperFactory 对象包装工厂
* @param reflectorFactory 反射工厂
*/
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
// 初始化被包装的原始对象
this.originalObject = object;
// 初始化实例化对象的工厂类
this.objectFactory = objectFactory;
// 初始化创建对象包装器的工厂类
this.objectWrapperFactory = objectWrapperFactory;
// 初始化用于获取类描述对象的工厂类
this.reflectorFactory = reflectorFactory;
// 为原始对象创建对象包装器
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);
}
}
在构造方法中,除了objectWrapper
属性之外,其余属性的初始化全都是简单的赋值操作。
本篇文章开头时有说过ObjectWrapper
是一个对象包装器接口定义,他为被包装对象提供了统一的操作属性的方法:
/**
* 对象包装器
*
* 包装对象后提供统一的属性操作方法。
*
* @author Clinton Begin
*/
public interface ObjectWrapper {
/**
* 根据属性名称描述符获取指定属性的值
*
* @param prop 属性名称描述符
* @return 指定属性的值
*/
Object get(PropertyTokenizer prop);
/**
* 根据属性名称描述符为指定的属性赋值
*
* @param prop 属性名称描述符
* @param value 指定的值
*/
void set(PropertyTokenizer prop, Object value);
/**
* 查找属性名称
*
* @param name 用于获取属性名的名称,比如可以通过name获取Name,或者Name获取name。
* @param useCamelCaseMapping 是否使用驼峰命名法
* @return 实际属性名称
*/
String findProperty(String name, boolean useCamelCaseMapping);
/**
* 获取所有可读属性名
*
* @return 可读属性名集合
*/
String[] getGetterNames();
/**
* 获取所有可写属性名
*
* @return 可写属性名集合
*/
String[] getSetterNames();
/**
* 获取指定属性的setter方法入参类型
*
* @param name 属性名
* @return 该属性setter方法入参类型
*/
Class<?> getSetterType(String name);
/**
* 获取指定属性的getter方法返回类型
*
* @param name 属性名称
* @return 该属性的getter方法的返回类型
*/
Class<?> getGetterType(String name);
/**
* 是否有指定属性的setter方法
*
* @param name 属性名
* @return 是否有指定属性的setter方法
*/
boolean hasSetter(String name);
/**
* 是否有指定属性的getter方法
*
* @param name 属性名称
* @return 是否有指定属性的getter方法
*/
boolean hasGetter(String name);
/**
* 实例化某个属性的值,并获取对应的MetaObject对象
*
* @param name 完整的属性名
* @param prop 属性名描述符
* @param objectFactory 对象创建工厂
* @return 指定的属性的值对应的MetaObject对象
*/
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
/**
* 判断被包装的对象是否是集合
*
* @return 被包装对象是否是集合
*/
boolean isCollection();
/**
* 往被包装的集合对象中添加新的元素
*
* @param element 被添加的元素
*/
void add(Object element);
/**
* 往被包装的集合对象中添加一组元素
*
* @param element 被添加的元素集合
* @param <E> 元素类型
*/
<E> void addAll(List<E> element);
}
因为不同的JAVA对象,其属性操作方法也有所不同,所以综合绝大多数会被操作的标准对象的特点,mybatis默认为ObjectWrapper
提供了四个实现类:BaseWrapper
、BeanWrapper
、MapWrapper
、CollectionWrapper
。
其中BaseWrapper
是一个抽象类,它定义了对象包装器中操作集合的通用方法,他有两个属性定义:NO_ARGUMENTS
和metaObject
。
/**
* 执行getter方法时使用的入参
*/
protected static final Object[] NO_ARGUMENTS = new Object[0];
/**
* 被包装对象的元数据
*/
protected final MetaObject metaObject;
其中名为NO_ARGUMENTS
的属性常作为方法入参被用于执行getter类型方法的反射操作,比如:
Invoker method = metaClass.getGetInvoker(prop.getName());
return method.invoke(object, NO_ARGUMENTS);
至于名为metaObject
的属性则负责维护被包装对象的元数据。
BeanWrapper
和MapWrapper
都是BaseWrapper
的实现类,BeanWrapper
用于包装普通的java对象,MapWrapper
负责包装Map
集合对象,至于另一个ObjectWrapper
的实现类CollectionWrapper
则负责包装普通集合对象。
在MetaObject
的构造方法中,对于objectWrapper
属性的赋值操作比较简单:
- 如果被包装的原始对象本身就是
ObjectWrapper
接口的实例的话,不进行任何处理,直接赋值给objectWrapper
属性。 - 如果被包装的原始对象本身不是
ObjectWrapper
接口的实例,那就优先使用ObjectWrapperFactory
对其进行包装处理, - 如果
ObjectWrapperFactory
也不能处理该对象,那么就按照该对象的实际类型将其包装成MapWrapper
、CollectionWrapper
或者BeanWrapper
类型的实例。
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);
}
通过上面的方法我们可以看到无论是MapWrapper
、CollectionWrapper
亦或是BeanWrapper
他们的构造方法都需要一个被包装对象的实例以及该实例对应的MetaObject
对象。
new MapWrapper(this, (Map) object);
new CollectionWrapper(this, (Collection) object);
new BeanWrapper(this, object);
在MapWrapper
的构造方法中:
public MapWrapper(MetaObject metaObject, Map<String, Object> map) {
super(metaObject);
this.map = map;
}
会首先调用其父类BaseWrapper
的构造方法来缓存当前MetaObject
对象,之后将需要被包装的Map
对象赋值给唯一的map
属性:
/**
* 被包装的Map集合对象
*/
private final Map<String, Object> map;
负责将MetaObject
对象保存起来的BaseWrapper
的构造方法实现也很简单:
/**
* 被包装对象的元数据
*/
protected final MetaObject metaObject;
protected BaseWrapper(MetaObject metaObject) {
this.metaObject = metaObject;
}
BaseWrapper
的另一个实现类BeanWrapper
的构造方法同样先调用了BaseWrapper
的构造方法保存MetaObject
实例,之后又保存了被缓存的对象以及该对象的类型元数据:
/**
* 原始对象
*/
private final Object object;
/**
* 类型元数据
*/
private final MetaClass metaClass;
public BeanWrapper(MetaObject metaObject, Object object) {
super(metaObject);
// 缓存被包装的原始对象
this.object = object;
// 获取原始对象的类型描述元数据
this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
}
最后,关于CollectionWrapper
的构造方法,则只是简单的将被包装的集合对象保存起来而已:
/**
* 被包装的集合对象
*/
private final Collection<Object> object;
public CollectionWrapper(MetaObject metaObject, Collection<Object> object) {
this.object = object;
}
在完成ObjectWrapper
实例的创建工作之后,MetaObject
会将其赋值给objectWrapper
属性,至此MetaObject
对象也创建完成了。
在完成了MetaObject
对象的创建工作之后,为了简化后续学习的复杂度,这里我们提前看一下MetaObject
对象的方法定义:
MetaObject
不仅对外暴露了objectFactory
、objectWrapperFactory
、reflectorFactory
、originalObject
以及objectWrapper
属性的getter
方法:
// 对外暴露五个属性的getter方法
public ObjectFactory getObjectFactory() {
return objectFactory;
}
public ObjectWrapperFactory getObjectWrapperFactory() {
return objectWrapperFactory;
}
public ReflectorFactory getReflectorFactory() {
return reflectorFactory;
}
public Object getOriginalObject() {
return originalObject;
}
public ObjectWrapper getObjectWrapper() {
return objectWrapper;
}
作为一个可以操作对象属性的元数据对象,自然少不了对外提供用于操作对象属性的方法,这些方法中大部分实现都是直接委托给了ObjectWrapper
的同名方法来完成的,
比如:
/**
* 查找属性属性名称
*
* @param propName 用于获取属性名的名称,比如可以通过name获取Name,或者Name获取name。
* @param useCamelCaseMapping 是否使用驼峰命名法,即下划线转驼峰,比如通过first_name 获取firstName属性
* @return 实际属性名称
*/
public String findProperty(String propName, boolean useCamelCaseMapping) {
return objectWrapper.findProperty(propName, useCamelCaseMapping);
}
/**
* 获取所有可读属性名
*
* @return 可读属性名集合
*/
public String[] getGetterNames() {
return objectWrapper.getGetterNames();
}
/**
* 获取所有可写属性名
*
* @return 可写属性名集合
*/
public String[] getSetterNames() {
return objectWrapper.getSetterNames();
}
/**
* 获取指定属性的setter方法入参类型
*
* @param name 属性名
* @return 该属性setter方法入参类型
*/
public Class<?> getSetterType(String name) {
return objectWrapper.getSetterType(name);
}
/**
* 获取指定属性的getter方法返回类型
*
* @param name 属性名称
* @return 该属性的getter方法的返回类型
*/
public Class<?> getGetterType(String name) {
return objectWrapper.getGetterType(name);
}
/**
* 是否有指定名称属性的setter方法
*
* @param name 指定属性名称
*/
public boolean hasSetter(String name) {
return objectWrapper.hasSetter(name);
}
/**
* 是否有指定名称属性的getter方法
*
* @param name 指定属性名称
*/
public boolean hasGetter(String name) {
return objectWrapper.hasGetter(name);
}
/**
* 维护的对象是否是集合类型
*/
public boolean isCollection() {
return objectWrapper.isCollection();
}
/**
* 往维护集合对象中添加一个元素
*/
public void add(Object element) {
objectWrapper.add(element);
}
/**
* 往维护集合对象中添加一组元素
*/
public <E> void addAll(List<E> list) {
objectWrapper.addAll(list);
}
剩余的方法则是在经过业务处理之后,才整体或者部分委托给ObjectWrapper
的实现类来完成:
/**
* 获取指定属性的值
*
* @param name 属性名称
* @return 属性值
*/
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);
}
}
/**
* 未指定的属性赋值
* @param name 属性名称
* @param value 属性值
*/
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// [param.id]
// 获取指定名称的值的对象元数据
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null) {
// don't instantiate child path if value is null
return;
} else {
// 实例化属性值
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
// 保存属性对应的值
metaValue.setValue(prop.getChildren(), value);
} else {
// [id]
// 保存值
objectWrapper.set(prop, value);
}
}
/**
* 将指定属性的值包装成元数据对象
*
* @param name 名称
* @return 指定名称的值的对象元数据
*/
public MetaObject metaObjectForProperty(String name) {
// 从对象元数据中获取指定名称的属性值
Object value = getValue(name);
// 生成该值对应的对象元数据
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
方法很多,我们慢慢看。
用于查找属性名称的findProperty方法
我们先来看用于查找属性名称的findProperty
方法,findProperty
有两个入参,一个是表示用于查找属性名称的属性名称描述符propName
,另一个表示在获取属性名称时是否将propName
中的下划线转换为驼峰之后再进行查找操作的useCamelCaseMapping
。
findProperty
方法的作用是在指定对象中解析出propName
参数的有效部分,那么什么叫做propName
参数的有效部分呢?
propName
是一个属性名称描述符,在mybatis中他通常会被转换为PropertyTokenizer
对象来使用,在学习过PropertyTokenizer
对象之后,我们了解到属性名称描述符可以用来描述多层嵌套的属性,比如: panda.name.first
。
关于
PropertyTokenizer
对象,如果不太了解,可以点击了解PropertyTokenizer对象
既然propName
可以描述多层嵌套的属性,那就有可能会产生属性描述符中的某一级属性并不存在的场景,比如:当propName
的值为panda.name.first
时,如果panda
中不存在name
属性定义,这时候panda
就是propName
参数的有效部分,而name.first
则是无效部分。
我们看一个下面这个示例:
/**
* 公司
*/
public static class Company {
private Staff ceo;
private Staff cto;
private Mascot mascot;
public Company(Staff ceo) {
this.ceo = ceo;
}
}
/**
* 员工
*/
public static class Staff {
/**
* 姓名
*/
private String name;
public Staff(String name) {
this.name = name;
}
}
/**
* 吉祥物
*/
private static class Mascot {
// 无属性定义
}
请仔细看下面这个测试方法的内容:
@Test
public void findPropertyTest() {
// 初始化实例对象
Company company = new Company(new Staff("jpanda"));
// 获取该对象的MetaObject
// SystemMetaObject后面会讲,他的作用是创建MetaObject对象
MetaObject metaObject = SystemMetaObject.forObject(company);
// 测试
// 有ceo属性,且ceo有name属性
String ceoName = metaObject.findProperty("ceo.name", false);
assert ceoName.equals("ceo.name");
// 有cto属性,且cto有name属性,虽然cto没有被赋值,但不影响属性名的解析
String ctoName = metaObject.findProperty("cto.name", false);
assert ctoName.equals("cto.name");
// 有mascot属性,但是mascot没有name属性
String mascotName = metaObject.findProperty("mascot.name", false);
assert mascotName.equals("mascot.");
// 无coo属性定义
String cooName = metaObject.findProperty("coo.name", false);
assert cooName == null;
}
在上面的示例代码中,我们不难发现,一个属性是否有效,不在于这个属性是否被赋值,被初始化,而在于他所对应的类中是否定义了该属性。
了解了的有效属性的概念,我们继续看findProperty
方法的具体实现。
MetaObject
的findProperty
方法是完全委托给了ObjectWrapper
的findProperty
方法来完成的。
因为ObjectWrapper
除去抽象的BaseWrapper
类定义之外,还有三个有效实现,所以相应的ObjectWrapper
的findProperty
方法也就有三种实现:
- 因为
CollectionWrapper
负责包装集合类型,而常用的集合类型通常被用来维护一组元素,这些集合类型的类定义中往往不会对外暴露有效的属性名称定义,甚至对于其维护的元素来讲,获取具体某个元素的操作通常也是使用集合下标index
来完成的,但是一方面Collection
接口的实现类Set
并不支持通过索引下标获取元素,另一方面通过索引下标获取数据还可能会触发IndexOutOfBoundsException
异常,因此CollectionWrapper
不支持调用findProperty
方法:@Override public String findProperty(String name, boolean useCamelCaseMapping) { throw new UnsupportedOperationException(); }
MapWrapper
用于维护Map
集合,虽然在Map
集合的类型定义中通常也不会对外暴露有效的属性定义,但是因为对Map
集合维护的元素的操作是统一的,往往是通过Map
集合维护的Entity
的key
值来操作相对应的value
,因此在一定程度上我们可以认为Map
集合所维护的每个Entity
的key
值就是属性名称,那么因为Map
对字符串类型的key
值没有什么限制,所以在理论上传入的属性名可以是任意字符串内容,不过需要注意的是因为mybatis在PropertyTokenizer
中为.
和[
两个符号赋予了特殊含义,所以需要尽可能避免使用这两个符号。因为
Map
的key
集合在运行期间才会确定,所以MapWrapper
的findProperty
方法会直接返回传入的属性名称本身:@Override public String findProperty(String name, boolean useCamelCaseMapping) { return name; }
至于用于包装其余JAVA对象
BeanWrapper
,这里的属性就是常规意义上的属性,因此,BeanWrapper
的findProperty
方法实现被委托给了MetaClass
的findProperty
方法来完成:@Override public String findProperty(String name, boolean useCamelCaseMapping) { return metaClass.findProperty(name, useCamelCaseMapping); }
在MetaClass
的findProperty
方法中,会根据useCamelCaseMapping
参数的值来决定是否移除属性名中的下划线,之后调用重载的findProperty(String)
方法来完成后续的操作:
public String findProperty(String name, boolean useCamelCaseMapping) {
if (useCamelCaseMapping) {
// 移除下划线
name = name.replace("_", "");
}
return findProperty(name);
}
在findProperty(String)
重载方法内,通过buildProperty
方法,来完成具体属性名称的处理操作:
public String findProperty(String name) {
StringBuilder prop = buildProperty(name, new StringBuilder());
return prop.length() > 0 ? prop.toString() : null;
}
buildProperty
方法会将属性名称转换为PropertyTokenizer
对象,然后根据PropertyTokenizer
对象是否有子节点分别执行两套不同的业务逻辑:
private StringBuilder buildProperty(String name, StringBuilder builder) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// 获取指定名称的属性
String propertyName = reflector.findPropertyName(prop.getName());
// 属性不存在,跳出递归
if (propertyName != null) {
// 属性存在
builder.append(propertyName);
builder.append(".");
// 获取该属性对应的类型元数据
MetaClass metaProp = metaClassForProperty(propertyName);
// 递归解析属性
metaProp.buildProperty(prop.getChildren(), builder);
}
} else {
// 获取属性名
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
如果
PropertyTokenizer
包含子节点,则调用Reflector
对象的findPropertyName
方法获取当前属性的真实名称:- 如果当前属性名称存在,则继续调用
metaClassForProperty
方法获取该属性对应的类型元数据,并将PropertyTokenizer
的子节点名称和builder
作为参数继续传递给MetaClass
的buildProperty
方法递归调用完成解析属性名称的工作。
/** * 获取指定属性的类型元数据 * * @param name 指定字段 * @return 指定字段的类型元数据 */ public MetaClass metaClassForProperty(String name) { // 从反射元数据中获取该名称的getter方法的类型 Class<?> propType = reflector.getGetterType(name); // 生成元数据 return MetaClass.forClass(propType, reflectorFactory); }
- 如果当前属性名称不存在,就表示对应的java类型中没有定义该属性,那么就结束递归操作,直接返回已经获取到属性名称,那么这个已经获取到的属性名称,就是前面提到过的有效属性名称。
- 如果当前属性名称存在,则继续调用
如果
PropertyTokenizer
不包含子节点,则调用Reflector
对象的findPropertyName
方法获取当前属性的真实名称,将其追加到builder
中返回。
@startuml
hide footbox
participant MetaClass as mc
participant PropertyTokenizer as rt
participant Reflector as r
autonumber 1 1
loop 有未处理的有效子属性
[-> mc :<color:red><b><size:16>buildProperty()
activate mc
mc -> rt ++: hasNext()
return 是否有子属性
alt 有子属性
mc -> r ++: findPropertyName()
return 大小写不敏感的属性名
note left
获取指定名称对应的
大小写不敏感的属性名
endnote
opt 存在对应的属性
activate mc
mc -> mc :metaClassForProperty()
note right
获取该属性对应的类型元数据:
一个新的MetaClass
end note
mc -> mc ++ : <color:red><b><size:16>buildProperty()
return 有效属性名
note right
递归调用MetaClass的buildProperty方法
完成子属性的解析
endnote
end
else 无子属性
mc -> r ++:findPropertyName()
return 大小写不敏感的属性名
end
end
[<- mc : 有效属性名
title 通过递归获取有效的属性名称
header mybatis 源码学习笔记
footer jpanda@aliyun.com jpanda.cn
@enduml
用于获取指定对象中所有可读属性名的getGetterNames方法
在了解了findProperty
方法之后,我们继续看用于获取指定对象中所有可读属性名的getGetterNames
方法:
/**
* 获取所有可读属性名
*
* @return 可读属性名集合
*/
public String[] getGetterNames() {
return objectWrapper.getGetterNames();
}
MetaObject
的getGetterNames
方法也是完全委托给了ObjectWrapper
的实现类来完成的,根据在解析findProperty
方法时梳理出的CollectionWrapper
、MapWrapper
以及BeanWrapper
三者对于属性名称
定义的不同含义,可以很容易的理解下面这些包装器对象的getGetterNames
方法实现:
CollectionWrapper
不支持属性名称,因此也就不支持getGetterNames
方法的调用:@Override public String[] getGetterNames() { throw new UnsupportedOperationException(); }
MapWrapper
将Entity
对象的key
值作为属性名称,因此会直接返回所有Entity
的key
值集合:@Override public String[] getGetterNames() { return map.keySet().toArray(new String[map.keySet().size()]); }
BeanWrapper
的属性就是常规意义上的属性名称,所以会真的去获取JAVA对象的所有可读属性名称:@Override public String[] getGetterNames() { return metaClass.getGetterNames(); }
三个实现中,也就BeanWrapper
稍显复杂一下,在实现上,BeanWrapper
将获取可读属性名称集合的操作委托了MetaClass
的getGetterNames
方法来完成:
public String[] getGetterNames() {
return reflector.getGetablePropertyNames();
}
MetaClass
又将实现委托给了Reflector
的getGetterNames
方法:
/**
* 获取可读属性名称集合
*
* @return 可读属性名称集合
*/
public String[] getGetablePropertyNames() {
return readablePropertyNames;
}
Reflector
的getGetterNames
方法则直接返回了用于维护可读属性名称集合的readablePropertyNames
属性。
Reflector
中属性的赋值操作在前文已经提到过。
@startuml
hide footbox
participant MetaObject as mo
participant BeanWrapper as bw
participant MetaClass as mc
participant Reflector as r
autonumber 1 1
[-> mo : getGetterNames()
activate mo
mo -> bw ++ : getGetterNames()
bw -> mc ++ : getGetterNames()
mc -> r ++ :getGetablePropertyNames()
return readablePropertyNames
return readablePropertyNames
return readablePropertyNames
[<- mo : 返回拥有<color:Blue>Getter</color>方法的属性名集合
note over mo,r
readablePropertyNames:
可读属性集合,该属性在<color:red>Reflector</color>对象的构造方法中完成初始化
end note
title 获取指定对象所有可读属性名
header mybatis 源码学习笔记
footer jpanda@aliyun.com jpanda.cn
@enduml
MetaObject
中用于获取所有可写属性名称集合的getSetterNames
方法实现和原理同getGetterNames
近乎一致:
/**
* 获取所有可写属性名
*
* @return 可写属性名集合
*/
public String[] getSetterNames() {
return objectWrapper.getSetterNames();
}
getSetterNames
方法也直接被委托给ObjectWrapper
来实现,基于同样的原因:
CollectionWrapper
不支持getSetterNames
方法。MapWrapper
会直接返回所有Entity
的key
值集合。BeanWrapper
的调用栈还是延续到Reflector
中,直到获取到Reflector
中负责维护可写属性名称集合的writeablePropertyNames
属性值。
用于操作属性值的getValue和setValue方法
MetaObject
还有两个用于操作属性值的方法:getValue
和setValue
。
getValue
负责获取属性描述符对应的属性值/** * 获取指定属性的值 * * @param name 属性名称 * @return 属性值 */ 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 { // 递归处理属性描述符 return metaValue.getValue(prop.getChildren()); } } else { // 真正获取属性值 return objectWrapper.get(prop); } }
setValue
负责为属性描述符对应的属性设值/** * 未指定的属性赋值 * @param name 属性名称 * @param value 属性值 */ public void setValue(String name, Object value) { PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { // [param.id] // 获取指定名称的值的对象元数据 MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); if (metaValue == SystemMetaObject.NULL_META_OBJECT) { if (value == null) { // 保存的值为null,没有必要实例化 // don't instantiate child path if value is null return; } else { // 实例化属性值 metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory); } } // 保存属性对应的值 metaValue.setValue(prop.getChildren(), value); } else { // [id] // 保存值 objectWrapper.set(prop, value); } }
getValue
和setValue
两个方法的在实现上有些类似,都是先解析出属性描述符对应的具体属性,然后交给ObjectWrapper
的同名方法完成取值或者赋值操作。
但是因为取值操作和赋值操作在本质上的不同,在多层嵌套的属性结构中,如果某一层属性值为null
,取值操作会直接返回null
,但是赋值操作则需要根据用于为属性设值的对象是否为null
来决定是否实例化对应的属性。
比如:
let panda={
"name":null
};
当我们获取panda.name.first
描述符对应的属性值时,我们会得到一个null
,当我们为panda.name.first
属性赋值时,我们会得到对象:
let panda={
"name":{
"first":"j"
}
};
这里就是getValue
和setValue
两个方法的主要区别之一,getValue
解析null
值属性时,直接返回null
即可,setValue
则需要进行进一步的判断。
在getValue
方法中,MetaObject
会将传入的属性名称描述符转换为PropertyTokenizer
对象,然后根据PropertyTokenizer
是否有子节点来执行不同的业务逻辑
如果PropertyTokenizer
有子节点定义,getValue
方法会利用metaObjectForProperty
方法依次获取每层属性值对应的对象元数据,在新的对象元数据实例不是SystemMetaObject.NULL_META_OBJECT
的前提下,递归调用MetaObject
的getValue
方法来完成相应的下一级属性值的获取操作。
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// 获取属性值对应的MetaObject
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
// 当前属性是空对象,子类自然不存在值
return null;
} else {
// 递归处理
return metaValue.getValue(prop.getChildren());
}
}
SystemMetaObject.NULL_META_OBJECT
用于表示空的对象元数据,关于SystemMetaObjec
相关的内容在后面会讲。
在metaObjectForProperty
方法中,会先利用MetaObject
的getValue
方法来完成获取属性值的操作,因此在这里metaObjectForProperty
和getValue
方法就构成了相互依赖的递归关系。
/**
* 将指定属性的值包装成元数据对象
*
* @param name 名称
* @return 指定名称的值的对象元数据
*/
public MetaObject metaObjectForProperty(String name) {
// 从对象元数据中获取指定名称的属性值
Object value = getValue(name);
// 生成该值对应的对象元数据
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
不过,因为getValue
方法调用metaObjectForProperty
时传入的参数是PropertyTokenizer
的indexedName
属性,indexedName
不可能是多层属性描述符:
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
所以当metaObjectForProperty
继续递归调用getValue
方法时,就会触发getValue
的第二种处理方式。
// 从对象元数据中获取指定名称的属性值
Object value = getValue(name);
// 生成该值对应的对象元数据
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
在metaObjectForProperty
调用getValue
方法获取到属性描述符对应的属性值之后,会借助于MetaObject
的forObject
方法将其包装成一个对象元数据返回给调用方。
MetaObject
的forObject
方法实现十分简单,他在被包装对象为null
值时将会返回SystemMetaObject.NULL_META_OBJECT
,非null
值时调用MetaObject
的构造参数完成对象元数据的构造工作。
/**
* 生成对象元数据
*
* @param object 对象
* @param objectFactory 对象工厂
* @param objectWrapperFactory 对象包装工厂
* @param reflectorFactory 反射工厂
* @return 对象元数据
*/
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);
}
}
MetaObject
的构造工作前面已经讲过,所以不再赘述。
继续看getValue
的第二种处理方式,该方式用于处理属性描述符描述单个属性的场景,具体的实现逻辑交给了ObjectWrapper
对象的get
方法来完成。
// 真正获取属性值
return objectWrapper.get(prop);
基于相同的理论,ObjectWrapper
的get
方法同样有三个实现,因为CollectionWrapper
不支持属性名称,所以也就不支持get
方法.
@Override
public Object get(PropertyTokenizer prop) {
throw new UnsupportedOperationException();
}
MapWrapper
和BeanWrapper
都是支持属性名称,所以他们提供了get
方法的实现,而且两者的实现比较类似,他们都会判断传入的PropertyTokenizer
参数是否有索引定义,如果有索引定义就按照集合的方式取值,如果没有的话,就按照常规的方式取值。
// MapWrapper
// TODO
// BeanWrapper
// TODO
MapWrapper
和BeanWrapper
对于集合的取值操作实现是一致的,他们都会调用父类BaseWrapper
中定义的resolveCollection
方法获取PropertyTokenizer
对应的属性集合,再利用BaseWrapper
的getCollectionValue
方法处理获取到的属性集合和PropertyTokenizer
参数,完成从集合中取值的操作:
if (prop.getIndex() != null) {
// 属性描述符中包含索引定义,从Map中获取到对应的属性集合
// 比如: prop=names[panda],resolveCollection会获取map集合中key为names的value。
Object collection = resolveCollection(prop, map);
// 从key为names的value属性集合中获取key为panda的属性值
return getCollectionValue(prop, collection);
}
这里有一点值得注意的是
ObjectWrapper
的get
方法的入参PropertyTokenizer
属性不会是多层属性描述符。
BaseWrapper
的resolveCollection
方法实现比较简单,他的作用就是从当前维护的对象元数据中取出指定的属性值,看代码实现会发现方法名叫resolveCollection
,但实际上该方法也能获取到普通的JAVA属性值。
/**
* 解析集合对象
*
* @param prop 属性名称
* @param object 实例值
* @return 最终对象的值
*/
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
// 如果属性名称为空,直接返回对象
return object;
} else {
// 继续从对象元数据中解析该属性
return metaObject.getValue(prop.getName());
}
}
getCollectionValue
方法负责根据传入的PropertyTokenizer
参数从指定的集合中取值:
/**
* 解析属性名称描述符获取集合内对应的值
*
* @param prop 属性描述符
* @param collection 集合
* @return 集合内对应的值
*/
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {
// 如果是map,直接根据key取值
return ((Map) collection).get(prop.getIndex());
} else {
// 获取下标
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.");
}
}
}
getCollectionValue
方法实现比较简单,唯一需要注意的就是如果该方法的入参collection
不是一个集合,将会导致异常的发生,比如下面这个场景:
// 数据对象
user={
"names":["j","panda"]
,"age":18
}
当我们获取通过names[0]
属性描述符获取user
对象的数据时能够正确获得值j
,但当我们通过age[0]
属性描述符获取user
对象的数据时将会导致异常的发生,这是因为age[0]
中包含了索引定义,所以会命中resolveCollection
方法来获取user
的age
属性,但是因为age
不是集合定义,所以在getCollectionValue
方法中就会导致异常的产生。
看完BeanWrapper
和MapWrapper
的集合取值,我们继续看一下BeanWrapper
和MapWrapper
的常规取值实现。
BeanWrapper
和MapWrapper
的常规取值实现略有不同,但是都很好理解,MapWrapper
是直接从他维护的map
属性中取值:
{
// 直接从map中取值
return map.get(prop.getName());
}
BeanWrapper
稍微复杂点,因为他维护的是真正意义上的JAVA属性定义,所以他在获取到属性定义之后还需要执行反射操作获取属性对应的值。
{
// 获取属性值
return getBeanProperty(prop, object);
}
在前面的文章中,我们已经了解mybatis会将关于操作属性值的反射操作进一步包装成
Invoker
执行器对象来使用。
BeanWrapper
获取属性值的具体实现是交给getBeanProperty
方法来完成的:
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
// 获取该属性对应的get方法反射操作包装器
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);
}
}
方法看起来很长,实际上就做了两件事:获取读取属性值的Invoker
执行器和通过执行器获取属性值。
获取Invoker
执行器的工作是由MetaClass
的getGetInvoker
方法委托给Reflector
对象的getGetInvoker
方法来完成的:
// MetaClass
public Invoker getGetInvoker(String name) {
return reflector.getGetInvoker(name);
}
Reflector
的getGetInvoker
方法直接从负责维护getter
方法集合和执行器的映射列表的getMethods
属性中获取到所需的执行器对象返回给调用方:
/**
* 获取指定属性的getter执行器
* @param propertyName 属性名称
* @return getter执行器
*/
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;
}
通过执行器获取属性值的操作也比较简单,直接执行获取到的Invoker
对象的invoke
方法即可,注意看在这里就用到了BaseWrapper
的NO_ARGUMENTS
属性:
// 执行反射操作,获取数据
return method.invoke(object, NO_ARGUMENTS);
关于
Invoker
对象的invoke
方法的不同实现之前已经讲过了,这里就不说了,
BeanWrapper
的getBeanProperty
方法从MetaClass
中获取该属性对应的getter
执行器,然后通过该执行器进行取值操作:
负责获取getter
执行器的MetaClass
的getGetInvoker
方法将具体实现委托给了Reflector
对象的getGetInvoker
方法来完成:
public Invoker getGetInvoker(String name) {
return reflector.getGetInvoker(name);
}
BeanWrapper
的getBeanProperty
方法在得到getter
执行器之后,会执行Invoker
对象的invoke
方法来完成读取属性值的工作,Invoker
执行器在前面已经讲过,此处不再赘述。
这样,我们就完成了MetaObject
的取值操作。
@startuml
hide footbox
participant MetaObject as mo
participant BeanWrapper as bw
participant MetaClass as mc
participant Reflector as r
autonumber 1 1
[-> mo : getValue()
activate mo
create participant PropertyTokenizer as pt
activate pt
mo -> pt : hasNext()
return 是否有子属性定义
alt 有子属性定义
mo -> mo++ : metaObjectForProperty()
note left
获取主属性对应的MataObject
end note
return 当前属性的MataObject对象
alt 当前属性是空对象,子类自然不存在值
[<- mo : 返回null
else 递归处理属性描述符
mo -> mo ++ :getValue()
return 真正获取到的属性值
end
else 无子属性定义
mo -> bw ++ : get()
alt 属性描述符中包含索引定义
bw -> bw ++: resolveCollection()
return 获取集合对象
bw -> bw : getCollectionValue()
return 返回索引在该集合对应的值
else 属性描述符中不包含索引定义
bw -> bw ++: getBeanProperty()
bw -> mc ++ : getGetInvoker()
create participant Invoker
mc -> Invoker++: invoke()
return 属性值
destroy Invoker
return 属性值
deactivate bw
bw -> mo : 返回属性值
end
destroy pt
[<- mo : 返回获取到的属性值
end
title 根据属性描述符获取指定对象的属性值
header mybatis 源码学习笔记
footer jpanda@aliyun.com jpanda.cn
@enduml
MetaObject
还有两个用于获取指定属性方法类型的方法定义,getGetterType
用于获取指定属性getter
方法的返回类型,getSetterType
用于获取指定属性的setter
方法的入参类型。
两个方法的实现也比较类似:
都是先将属性描述符转换为PropertyTokenizer
对象,然后借助于MetaObject
的metaObjectForProperty
依次解析出每一层属性描述符对应的类型和子属性名称 ,在获取到最终的子属性名称之后,调用子属性所属对象的MetaClass
实例的getSetterType
,完成最终的类型取值操作。
@startuml
hide footbox
participant MetaObject as mo
participant BeanWrapper as bw
participant MetaClass as mc
autonumber 1 1
[-> mo : getGetterType()
activate mo
mo -> bw ++ : getGetterType()
create participant PropertyTokenizer as pt
bw -> pt ++ : hasNext()
alt 有子属性定义
bw -> mo ++ : metaObjectForProperty()
return 属性描述对应的当前属性对象元数据
alt null对象定义
bw -> mc ++ : getGetterType()
return 属性类型
else 非null对象定义
bw -> mo++ : getGetterType()
return 属性类型
end
else 无子属性定义
bw -> mc ++ : getGetterType()
return 属性类型
end
bw -> mo : 属性类型
[<- mo :属性类型
title 根据属性描述符获取指定对象的属性类型定义
header mybatis 源码学习笔记
footer jpanda@aliyun.com jpanda.cn
@enduml
上面的流程图没有展示MetaClass
解析类下单具体操作,我们再看一下metaClass
是如何解析类型的:
@startuml
hide footbox
participant BeanWrapper as bw
participant MetaClass as mc
participant Reflector as r
autonumber 1 1
[-> mc : getGetterType()
activate mc
create participant PropertyTokenizer as pt
mc -> pt ++ : hasNext()
alt 有子属性定义
mc -> mc ++ : metaClassForProperty()
return 新的MetaClass实例
mc -> mc ++ : getGetterType()
return 递归解析,获取属性描述符对应的类型
else 无子属性定义
mc -> r ++ : getGetterType()
opt 属性为集合类型且属性描述符包含索引定义
r -> r++: getGenericGetterType()
return 集合中字属性类型
end
return 属性描述符对应的类型
end
[<- mc : 属性描述符对应的类型
title 根据属性描述符获取指定对象的属性类型
header mybatis 源码学习笔记
footer jpanda@aliyun.com jpanda.cn
@enduml
在初步了解了ObjectWrapperFactory
对象相关信息之后,我们来看一下objectWrapperFactory
的DTD
定义和解析工作。
objectWrapperFactory
的DTO定义如下:
<!ELEMENT configuration (..., objectWrapperFactory?, ...)>
<!ELEMENT objectWrapperFactory EMPTY>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>
在mybatis的全局配置文件内,最多只能配置一个objectWrapperFactory
节点,objectWrapperFactory
有一个必填的type
属性,该属性用于指向一个ObjectWrapperFactory
的实例,这里可以使用实例别名。
XmlConfigBuilder
的parseConfiguration
方法负责触发objectWrapperFactoryt
节点的解析工作:
private void parseConfiguration(XNode root) {
// ...
// 配置对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// ...
}
/**
* 解析objectWrapperFactory节点
*
* @param context objectWrapperFactory节点
*/
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
configuration.setObjectWrapperFactory(factory);
}
}
objectWrapperFactoryElement
方法的实现也很简单,通过反射获取ObjectWrapperFactory
的实例之后, 交给Configuration
对象的方法setObjectWrapperFactory
将获取到的ObjectWrapperFactory
实例赋值给Configuration
的objectWrapperFactory
的属性,取代默认的DefaultObjectWrapperFactory
实现。
/**
* 配置对象包装工厂
* @param objectWrapperFactory 对象包装工厂
*/
public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
}
到此,ObjectWrapperFactory
元素的解析工作也就完成了。
这一章的内容比较多,但是了解了这里的知识之后,在后续的源码过程中,可以大大降低问题理解的难度。