一钉鸯、簡介
??在對 ObjectWrapper 類的介紹中,除了 #hasGetter
、#hasSetter
、#getGetterType
、#getSetterType
這幾個方法舀射,其余帶 PropertyTokenizer 類型參數(shù)的方法中,參數(shù)代表的表達(dá)式已經(jīng)是經(jīng)過解析沒有子表達(dá)式的形式了如 order[0]
怀伦,最終在 ObjectWrapper 中完成功能實(shí)現(xiàn)的最后一環(huán)脆烟,而表達(dá)式帶多層子表達(dá)式的初始狀態(tài)的解析過程正是在 MetaObject
類中進(jìn)行的,對 MetaObject 的分析房待,聚焦于這個屬性表達(dá)式的解析過程邢羔。
二、數(shù)據(jù)結(jié)構(gòu)
private Object originalObject; // 原始JavaBean對象
private ObjectWrapper objectWrapper; // 封裝了originalObject對象的包裝對象
private ObjectFactory objectFactory; // 負(fù)責(zé)實(shí)例化originalObject對象的工廠對象
private ObjectWrapperFactory objectWrapperFactory; // 負(fù)責(zé)創(chuàng)建ObjectWrapper的工廠對象
private ReflectorFactory reflectorFactory; // 用于創(chuàng)建并緩存Reflcetor對象的工廠對象
-
originalObject
:原始JavaBean對象桑孩。 -
objectWrapper
:原始JavaBean對象對應(yīng)的包裝對象拜鹤,根據(jù)JavaBean的類型,可能為 BeanWrapper流椒、MapWrapper敏簿、CollectionWrapper。 -
objectFactory
:負(fù)責(zé)實(shí)例化originalObject對象的工廠對象,通常發(fā)生在使用表達(dá)式指定屬性值時惯裕,最末尾的屬性所在的對象為空的時候用到温数,這時候需要先實(shí)例化對象,如表達(dá)式user.order.amount
蜻势,如果想給該表達(dá)式對應(yīng)的屬性賦值撑刺,如果中間的 order 對象是空的,則需要先實(shí)例化 order握玛,否則無法賦值猜煮。 -
objectWrapperFactory
:負(fù)責(zé)創(chuàng)建ObjectWrapper的工廠對象,默認(rèn)實(shí)現(xiàn)拋出異常败许,主要方便用戶自定義 ObjectWrapperFactory 的實(shí)現(xiàn)。 -
reflectorFactory
:用于創(chuàng)建并緩存Reflcetor對象的工廠對象淑蔚。
三市殷、構(gòu)造方法
// 構(gòu)造方法聲明為私有,只允許通過 MetaObject.forObject() 靜態(tài)方法創(chuàng)建 MetaObject 對象
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
// 若原始對象已經(jīng)是ObjectWrapper對象刹衫,則直接使用
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
// 若objectWrapperFactory能夠?yàn)樵撛紝ο髣?chuàng)建對應(yīng)的ObjectWrapper對象醋寝,則優(yōu)先使用
// objectWrapperFactory,而DefaultObjectWrapperFactory.hasWrapperFor()始終返回false
// 用戶可以自定義ObjectWrapperFactory實(shí)現(xiàn)進(jìn)行擴(kuò)展带迟。
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
// 若原始對象為Map類型音羞,則創(chuàng)建MapWrapper對象
} else if (object instanceof Map) {
this.objectWrapper = new MapWrapper(this, (Map) object);
// 若原始對象是Collection類型,則創(chuàng)建CollectionWrapper對象
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
// 若原始對象是普通的JavaBean對象仓犬,則創(chuàng)建BeanWrapper對象
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
【解析】
??如果傳入的對象是一個 ObjectWrapper 則直接使用嗅绰,否則嘗試用 ObjectWrapperFactory 創(chuàng)建 ObjectWrapper(假如用戶自定義實(shí)現(xiàn)可用),如果以上情況都不滿足搀继,則先后嘗試創(chuàng)建與對象類型相匹配的 ObjectWrapper窘面,可選類型為 MapWrapper、CollectionWrapper叽躯、BeanWrapper财边。
四、方法功能
1点骑、MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory)
【功能】創(chuàng)建 MetaObject 對象的靜態(tài)方法酣难,也是唯一調(diào)用途徑,因?yàn)闃?gòu)造方法為 private黑滴。
【源碼與注解】
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
// 若Object為空憨募,則統(tǒng)一返回 SystemMetaObject.NULL_META_OBJECT
// 反過來,若判斷到一個 JavaBean 對象對應(yīng)的 MetaObject 為 SystemMetaObject.NULL_META_OBJECT跷跪,可以判斷對象為空
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
【解析】
??如果原始Java對象不為空馋嗜,則調(diào)用構(gòu)造方法創(chuàng)建對象,否則吵瞻,使用特殊的用來表達(dá)原始對象為空的 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)
【功能】獲取表達(dá)式指定的原始對象中屬性的值。
【源碼與注解】
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name); // 解析表達(dá)式
if (prop.hasNext()) {
// 創(chuàng)建子表達(dá)式所屬對象
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
// 如果所屬對象為空眯停,對象下的屬性肯定也為空
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
// 不為空济舆,則遞歸解析子表達(dá)式,這里是遞歸的入口
return metaValue.getValue(prop.getChildren());
}
} else {
// 沒有子表達(dá)式或者到了遞歸出口莺债,直接調(diào)用 ObjectWrapper 對象獲得屬性值
return objectWrapper.get(prop);
}
}
【解析】
??遞歸處理表達(dá)式的處理跟 ObjectWrapper 中的 #hasGetter
滋觉、#hasSetter
、#getGetterType
齐邦、#getSetterType
類似椎侠,MetaClass 類中有個方法 #metaClassForProperty()
用來為對象屬性創(chuàng)建對應(yīng)的 MetaClass 對象,同樣的 MetaObject 類中也有方法 #metaObjectForProperty()
用來為對象屬性創(chuàng)建對應(yīng)的 MetaObject 對象措拇,其源碼如下:
public MetaObject metaObjectForProperty(String name) {
Object value = getValue(name);
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
3我纪、void setValue(String name, Object value)
【功能】為表達(dá)式指定的原始對象中屬性設(shè)置值。
【源碼與注解】
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
// 當(dāng)頂層屬性對象值為空時丐吓,依然嘗試去先創(chuàng)建初始化頂層屬性對象
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
// 如果要設(shè)定的值為空并且子表達(dá)不為空浅悉,這時沒有設(shè)置的必要了
// 為什么非要并且子表達(dá)式為非空?因?yàn)橛锌赡芫褪且o某個屬性設(shè)空值
if (value == null && prop.getChildren() != null) {
// don't instantiate child path if value is null
return;
} else {
// 初始化頂層屬性對象
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
// 如果值不為空券犁,直接調(diào)用术健;
// 如果值為空,則先初始化后再調(diào)用
metaValue.setValue(prop.getChildren(), value);
} else {
// 沒有子表達(dá)式或者到了遞歸出口粘衬,直接調(diào)用 ObjectWrapper 對象設(shè)置屬性值
objectWrapper.set(prop, value);
}
}
【解析】
??#setValue()
跟 #getValue()
差別在于:如果頂層屬性為空荞估,肯定無法獲取值,直接返回null稚新;但是設(shè)置屬性值泼舱,即使頂層屬性為空,也可以先初始化頂層屬性枷莉,再賦值娇昙。比如 user.order.amount
,屬性 user 是一個 User 對象笤妙,order 是其成員冒掌,類型為 Order,如果 order 為空蹲盘,獲取值時 order 的 amount 成員也必為空股毫,但是設(shè)置值時,即使 order 為空召衔,也可以先創(chuàng)建對象 order = new Order() 再給 order.amount 賦值铃诬。
4、其他方法
??除了上述方法,其他方法只是類中定義屬性的一個簡單的 getter趣席,或者是對 ObjectWrapper 接口方法的一個代理兵志,方便外部程序直接調(diào)用 MetaObject 類中的方法就可以實(shí)現(xiàn)相應(yīng)的功能。
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]"));
}