[MyBatis源码分析 - 反射器模块 - 组件八] Meta
一、简介
在对 ObjectWrapper 类的介绍中,除了 #hasGetter
、#hasSetter
、#getGetterType
、#getSetterType
这几个方法,其余带 PropertyTokenizer 类型参数的方法中,参数代表的表达式已经是经过解析没有子表达式的形式了如 order[0]
,最终在 ObjectWrapper 中完成功能实现的最后一环,而表达式带多层子表达式的初始状态的解析过程正是在 MetaObject
类中进行的,对 MetaObject 的分析,聚焦于这个属性表达式的解析过程。
二、数据结构
private Object originalObject; // 原始JavaBean对象
private ObjectWrapper objectWrapper; // 封装了originalObject对象的包装对象
private ObjectFactory objectFactory; // 负责实例化originalObject对象的工厂对象
private ObjectWrapperFactory objectWrapperFactory; // 负责创建ObjectWrapper的工厂对象
private ReflectorFactory reflectorFactory; // 用于创建并缓存Reflcetor对象的工厂对象
-
originalObject
:原始JavaBean对象。 -
objectWrapper
:原始JavaBean对象对应的包装对象,根据JavaBean的类型,可能为 BeanWrapper、MapWrapper、CollectionWrapper。 -
objectFactory
:负责实例化originalObject对象的工厂对象,通常发生在使用表达式指定属性值时,最末尾的属性所在的对象为空的时候用到,这时候需要先实例化对象,如表达式user.order.amount
,如果想给该表达式对应的属性赋值,如果中间的 order 对象是空的,则需要先实例化 order,否则无法赋值。 -
objectWrapperFactory
:负责创建ObjectWrapper的工厂对象,默认实现抛出异常,主要方便用户自定义 ObjectWrapperFactory 的实现。 -
reflectorFactory
:用于创建并缓存Reflcetor对象的工厂对象。
三、构造方法
// 构造方法声明为私有,只允许通过 MetaObject.forObject() 静态方法创建 MetaObject 对象
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;
// 若objectWrapperFactory能够为该原始对象创建对应的ObjectWrapper对象,则优先使用
// objectWrapperFactory,而DefaultObjectWrapperFactory.hasWrapperFor()始终返回false
// 用户可以自定义ObjectWrapperFactory实现进行扩展。
} 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);
// 若原始对象是普通的JavaBean对象,则创建BeanWrapper对象
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
【解析】
如果传入的对象是一个 ObjectWrapper 则直接使用,否则尝试用 ObjectWrapperFactory 创建 ObjectWrapper(假如用户自定义实现可用),如果以上情况都不满足,则先后尝试创建与对象类型相匹配的 ObjectWrapper,可选类型为 MapWrapper、CollectionWrapper、BeanWrapper。
四、方法功能
1、MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory)
【功能】创建 MetaObject 对象的静态方法,也是唯一调用途径,因为构造方法为 private。
【源码与注解】
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
// 若Object为空,则统一返回 SystemMetaObject.NULL_META_OBJECT
// 反过来,若判断到一个 JavaBean 对象对应的 MetaObject 为 SystemMetaObject.NULL_META_OBJECT,可以判断对象为空
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
【解析】
如果原始Java对象不为空,则调用构造方法创建对象,否则,使用特殊的用来表达原始对象为空的 SystemMetaObject.NULL_META_OBJECT
,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
}
private static class NullObject {
}
public static MetaObject forObject(Object object) {
return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
}
}
2、Object getValue(String name)
【功能】获取表达式指定的原始对象中属性的值。
【源码与注解】
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 {
// 没有子表达式或者到了递归出口,直接调用 ObjectWrapper 对象获得属性值
return objectWrapper.get(prop);
}
}
【解析】
递归处理表达式的处理跟 ObjectWrapper 中的 #hasGetter
、#hasSetter
、#getGetterType
、#getSetterType
类似,MetaClass 类中有个方法 #metaClassForProperty()
用来为对象属性创建对应的 MetaClass 对象,同样的 MetaObject 类中也有方法 #metaObjectForProperty()
用来为对象属性创建对应的 MetaObject 对象,其源码如下:
public MetaObject metaObjectForProperty(String name) {
Object value = getValue(name);
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
3、void setValue(String name, Object value)
【功能】为表达式指定的原始对象中属性设置值。
【源码与注解】
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
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 {
// 没有子表达式或者到了递归出口,直接调用 ObjectWrapper 对象设置属性值
objectWrapper.set(prop, value);
}
}
【解析】
#setValue()
跟 #getValue()
差别在于:如果顶层属性为空,肯定无法获取值,直接返回null;但是设置属性值,即使顶层属性为空,也可以先初始化顶层属性,再赋值。比如 user.order.amount
,属性 user 是一个 User 对象,order 是其成员,类型为 Order,如果 order 为空,获取值时 order 的 amount 成员也必为空,但是设置值时,即使 order 为空,也可以先创建对象 order = new Order() 再给 order.amount 赋值。
4、其他方法
除了上述方法,其他方法只是类中定义属性的一个简单的 getter,或者是对 ObjectWrapper 接口方法的一个代理,方便外部程序直接调用 MetaObject 类中的方法就可以实现相应的功能。
public ObjectFactory getObjectFactory() {
return objectFactory;
}
public ObjectWrapperFactory getObjectWrapperFactory() {
return objectWrapperFactory;
}
public ReflectorFactory getReflectorFactory() {
return reflectorFactory;
}
public Object getOriginalObject() {
return originalObject;
}
public String findProperty(String propName, boolean useCamelCaseMapping) {
return objectWrapper.findProperty(propName, useCamelCaseMapping);
}
public String[] getGetterNames() {
return objectWrapper.getGetterNames();
}
public String[] getSetterNames() {
return objectWrapper.getSetterNames();
}
public Class<?> getSetterType(String name) {
return objectWrapper.getSetterType(name);
}
public Class<?> getGetterType(String name) {
return objectWrapper.getGetterType(name);
}
public boolean hasSetter(String name) {
return objectWrapper.hasSetter(name);
}
public boolean hasGetter(String name) {
return objectWrapper.hasGetter(name);
}
public ObjectWrapper getObjectWrapper() {
return objectWrapper;
}
public boolean isCollection() {
return objectWrapper.isCollection();
}
public void add(Object element) {
objectWrapper.add(element);
}
public <E> void addAll(List<E> list) {
objectWrapper.addAll(list);
}
五、测试案例
1、类定义
public class RichType {
private RichType richType;
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;
}
}
2、测试案例
@Test
public void shouldGetAndSetField() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richField", "foo");
assertEquals("foo", meta.getValue("richField"));
}
@Test
public void shouldGetAndSetNestedField() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richField", "foo");
assertEquals("foo", meta.getValue("richType.richField"));
}
@Test
public void shouldGetAndSetProperty() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richProperty", "foo");
assertEquals("foo", meta.getValue("richProperty"));
}
@Test
public void shouldGetAndSetNestedProperty() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richProperty", "foo");
assertEquals("foo", meta.getValue("richType.richProperty"));
}
@Test
public void shouldGetAndSetMapPair() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richMap.key", "foo");
assertEquals("foo", meta.getValue("richMap.key"));
}
@Test
public void shouldGetAndSetMapPairUsingArraySyntax() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richMap[key]", "foo");
assertEquals("foo", meta.getValue("richMap[key]"));
}
@Test
public void shouldGetAndSetNestedMapPair() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richMap.key", "foo");
assertEquals("foo", meta.getValue("richType.richMap.key"));
}
@Test
public void shouldGetAndSetNestedMapPairUsingArraySyntax() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richMap[key]", "foo");
assertEquals("foo", meta.getValue("richType.richMap[key]"));
}
@Test
public void shouldGetAndSetListItem() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richList[0]", "foo");
assertEquals("foo", meta.getValue("richList[0]"));
}
@Test
public void shouldSetAndGetSelfListItem() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richList[0]", "foo");
assertEquals("foo", meta.getValue("richList[0]"));
}
@Test
public void shouldGetAndSetNestedListItem() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
meta.setValue("richType.richList[0]", "foo");
assertEquals("foo", meta.getValue("richType.richList[0]"));
}
@Test
public void shouldGetReadablePropertyNames() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
String[] readables = meta.getGetterNames();
assertEquals(5, readables.length);
for (String readable : readables) {
assertTrue(meta.hasGetter(readable));
assertTrue(meta.hasGetter("richType." + readable));
}
assertTrue(meta.hasGetter("richType"));
}
@Test
public void shouldGetWriteablePropertyNames() {
RichType rich = new RichType();
MetaObject meta = SystemMetaObject.forObject(rich);
String[] writeables = meta.getSetterNames();
assertEquals(5, writeables.length);
for (String writeable : writeables) {
assertTrue(meta.hasSetter(writeable));
assertTrue(meta.hasSetter("richType." + writeable));
}
assertTrue(meta.hasSetter("richType"));
}
@Test
public void shouldSetPropertyOfNullNestedProperty() {
MetaObject richWithNull = SystemMetaObject.forObject(new RichType());
richWithNull.setValue("richType.richProperty", "foo");
assertEquals("foo", richWithNull.getValue("richType.richProperty"));
}
@Test
public void shouldSetPropertyOfNullNestedPropertyWithNull() {
MetaObject richWithNull = SystemMetaObject.forObject(new RichType());
richWithNull.setValue("richType.richProperty", null);
assertEquals(null, richWithNull.getValue("richType.richProperty"));
}
@Test
public void shouldGetPropertyOfNullNestedProperty() {
MetaObject richWithNull = SystemMetaObject.forObject(new RichType());
assertNull(richWithNull.getValue("richType.richProperty"));
}
@Test
public void shouldVerifyHasReadablePropertiesReturnedByGetReadablePropertyNames() {
MetaObject object = SystemMetaObject.forObject(new Author());
for (String readable : object.getGetterNames()) {
assertTrue(object.hasGetter(readable));
}
}
@Test
public void shouldVerifyHasWriteablePropertiesReturnedByGetWriteablePropertyNames() {
MetaObject object = SystemMetaObject.forObject(new Author());
for (String writeable : object.getSetterNames()) {
assertTrue(object.hasSetter(writeable));
}
}
@Test
public void shouldSetAndGetProperties() {
MetaObject object = SystemMetaObject.forObject(new Author());
object.setValue("email", "test");
assertEquals("test", object.getValue("email"));
}
@Test
public void shouldVerifyPropertyTypes() {
MetaObject object = SystemMetaObject.forObject(new Author());
assertEquals(6, object.getSetterNames().length);
assertEquals(int.class, object.getGetterType("id"));
assertEquals(String.class, object.getGetterType("username"));
assertEquals(String.class, object.getGetterType("password"));
assertEquals(String.class, object.getGetterType("email"));
assertEquals(String.class, object.getGetterType("bio"));
assertEquals(Section.class, object.getGetterType("favouriteSection"));
}
@Test
public void shouldDemonstrateDeeplyNestedMapProperties() {
HashMap<String, String> map = new HashMap<String, String>();
MetaObject metaMap = SystemMetaObject.forObject(map);
assertTrue(metaMap.hasSetter("id"));
assertTrue(metaMap.hasSetter("name.first"));
assertTrue(metaMap.hasSetter("address.street"));
assertFalse(metaMap.hasGetter("id"));
assertFalse(metaMap.hasGetter("name.first"));
assertFalse(metaMap.hasGetter("address.street"));
metaMap.setValue("id", "100");
metaMap.setValue("name.first", "Clinton");
metaMap.setValue("name.last", "Begin");
metaMap.setValue("address.street", "1 Some Street");
metaMap.setValue("address.city", "This City");
metaMap.setValue("address.province", "A Province");
metaMap.setValue("address.postal_code", "1A3 4B6");
assertTrue(metaMap.hasGetter("id"));
assertTrue(metaMap.hasGetter("name.first"));
assertTrue(metaMap.hasGetter("address.street"));
assertEquals(3, metaMap.getGetterNames().length);
assertEquals(3, metaMap.getSetterNames().length);
Map<String,String> name = (Map<String,String>) metaMap.getValue("name");
Map<String,String> address = (Map<String,String>) metaMap.getValue("address");
assertEquals("Clinton", name.get("first"));
assertEquals("1 Some Street", address.get("street"));
}
@Test
public void shouldDemonstrateNullValueInMap() {
HashMap<String, String> map = new HashMap<String, String>();
MetaObject metaMap = SystemMetaObject.forObject(map);
assertFalse(metaMap.hasGetter("phone.home"));
metaMap.setValue("phone", null);
assertTrue(metaMap.hasGetter("phone"));
// hasGetter returns true if the parent exists and is null.
assertTrue(metaMap.hasGetter("phone.home"));
assertTrue(metaMap.hasGetter("phone.home.ext"));
assertNull(metaMap.getValue("phone"));
assertNull(metaMap.getValue("phone.home"));
assertNull(metaMap.getValue("phone.home.ext"));
metaMap.setValue("phone.office", "789");
assertFalse(metaMap.hasGetter("phone.home"));
assertFalse(metaMap.hasGetter("phone.home.ext"));
assertEquals("789", metaMap.getValue("phone.office"));
assertNotNull(metaMap.getValue("phone"));
assertNull(metaMap.getValue("phone.home"));
}
@Test
public void shouldNotUseObjectWrapperFactoryByDefault() {
MetaObject meta = SystemMetaObject.forObject(new Author());
assertTrue(!meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
}
@Test
public void shouldUseObjectWrapperFactoryWhenSet() {
MetaObject meta = MetaObject.forObject(new Author(), SystemMetaObject.DEFAULT_OBJECT_FACTORY, new CustomBeanWrapperFactory(), new DefaultReflectorFactory());
assertTrue(meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
// Make sure the old default factory is in place and still works
meta = SystemMetaObject.forObject(new Author());
assertFalse(meta.getObjectWrapper().getClass().equals(CustomBeanWrapper.class));
}
@Test
public void shouldMethodHasGetterReturnTrueWhenListElementSet() {
List<Object> param1 = new ArrayList<Object>();
param1.add("firstParam");
param1.add(222);
param1.add(new Date());
Map<String, Object> parametersEmulation = new HashMap<String, Object>();
parametersEmulation.put("param1", param1);
parametersEmulation.put("filterParams", param1);
MetaObject meta = SystemMetaObject.forObject(parametersEmulation);
assertEquals(param1.get(0), meta.getValue("filterParams[0]"));
assertEquals(param1.get(1), meta.getValue("filterParams[1]"));
assertEquals(param1.get(2), meta.getValue("filterParams[2]"));
assertTrue(meta.hasGetter("filterParams[0]"));
assertTrue(meta.hasGetter("filterParams[1]"));
assertTrue(meta.hasGetter("filterParams[2]"));
}