Ognl 中PropertyAccessor 用于从类实例中获取或设置属性值
Ognl中已有的PropertyAccessor :
因为不同的类的获取属性值和赋值方式不同,如Map、List、javabean,因此有多个子类,第一个子类对应一种类型。
Struts 对PropertyAccessor 的扩展:
Struts 加载扩展的PropertyAccessor :
Struts 中在OgnlValueStackFactory中调用OgnlRuntime.setPropertyAccessor(Class class,PropertyAccessor 实例)方法来加载Struts中定义的PropertyAccess。
在OgnlValueStackFactory 实例化之后,注入Containter时:
@Inject public void setContainer(Container container) throws ClassNotFoundException { Set<String> names = container.getInstanceNames(PropertyAccessor.class); for (String name : names) { Class cls = Class.forName(name); if (cls != null) { if (Map.class.isAssignableFrom(cls)) { PropertyAccessor acc = container.getInstance(PropertyAccessor.class, name); } OgnlRuntime.setPropertyAccessor(cls, container.getInstance(PropertyAccessor.class, name)); if (compoundRootAccessor == null && CompoundRoot.class.isAssignableFrom(cls)) { compoundRootAccessor = (CompoundRootAccessor) container.getInstance(PropertyAccessor.class, name); } } } //省略其它代码 }
那么Container中的PropertyAccessor类是从哪加载进来的呢??
是由Struts-default.xml中bean定义中加载的:
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" /> <bean type="ognl.PropertyAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Iterator" class="com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Enumeration" class="com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.List" class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Set" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Map" class="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="java.util.Collection" class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.ognl.ObjectProxy" class="com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor" />
xml中 type=ognl.PropertyAccessor,说明Class描述的类是一个PropertyAccessor类型
class=com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor,说明此类是PropertyAccessor类型的实现类
name=java.util.Map,说明PropertyAccessor实现类是对Map取值或赋值的。
它把setProperty和getProperty的职责委托给父类,父类getProerty取值之后,ObjectAccessor会在Context上下文中记录当前处理的类及属性名。
对于自定义类的属性的存取,首先会调用ObjectAccessor,获取属性,再从该属性中获取值时才用到其它PropertyAccessor,所以在ObjectAccessor记录类名及属性名,之后其它PropertyAccessor才会得到。
@Override public Object getProperty(Map map, Object o, Object o1) throws OgnlException { Object obj = super.getProperty(map, o, o1); map.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, o.getClass()); map.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, o1.toString()); ReflectionContextState.updateCurrentPropertyPath(map, o1); return obj; } @Override public void setProperty(Map map, Object o, Object o1, Object o2) throws OgnlException { super.setProperty(map, o, o1, o2); }
二、 XWorkEnumerationAccessor
它扩展了EnumerationPropertyAccessor,EnumerationPropertyAccessor的setPropert方法直接抛出异常,因为Enumeration接口没有赋值方法,XWorkEnumerationAccessor的扩展,将setProperty功能委托给ObjectPropertyAccessor,去查找可能有的set方法,如果有就调用。原因是操作的实例可能不是一个单纯的Enumeration.
ObjectPropertyAccessor opa = new ObjectPropertyAccessor(); @Override public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException { opa.setProperty(context, target, name, value); }
Enumeration对象,表达式可用属性: next|nextElement,hasNext|hasMoreElements
三、XWorkIteratorPropertyAccessor
它扩展了IteratorPropertyAccessor,父类IteratorPropertyAccessor的setProperty方法直接抛出异常,因为Iterator接口没有赋值方法,XWorkIteratorPropertyAccessor的扩展,将setProperty功能委托给ObjectPropertyAccessor,去查找可能有的set方法,如果有就调用。原因是操作的实例可能不是一个单纯的Iterator
ObjectPropertyAccessor opa = new ObjectPropertyAccessor(); @Override public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException { opa.setProperty(context, target, name, value); }
Iterator对象,表达式可用属性: next,hasNext
四、XWorkCollectionPropertyAccessor
它扩展了SetPropertyAccessor,一般情况下,表达式格式:collectionName['index'], 那么getPropertya和setProperty都交给父类处理。
特殊情况:表达式格式为collectionName.get('id'),即把collection当成Map,key=Collection中元素的主键属性值,通过“元素的主键属性值”对比"id"值而获取Collection中的元素。
前提是:
1.配置Collection中元素中哪一个属性做为主键属性?有如下几种方式:
a.Collection属性、Collection属性的set/get方法上标注的KeyProperty annotation
b.配置properties文件,文件名为“Collection属性所在类的全名 -conversion.properties”,文件位置: Collection属性所在类的类路径下,文件格式:KeyProperty_Collection属性名=Collection中元素的主键属性名
c.在Collection所在类上标注Annotation:
@Conversion(conversions={ @TypeConversion(rule=ConversionRule.KEY_PROPERTY,key = "KeyProperty_Collection属性名", value= "Collection中元素的主键属性名") } )
d.在Collection属性的get/set方法上标注
TypeConversion Annotation
@TypeConversion(rule=ConversionRule.KEY_PROPERTY,key = "KeyProperty_Collection属性名", value= "Collection中元素的主键属性名")
2.配置Collection中元素类型(通过元素类型+主键属性名反射出主键属性类型,把值转换为主键属性的正确类型,以便比较),有如下几种配置方式:
a.Collection属性、Collection属性的set/get方法上标注的Element annotation
b.配置properties文件,文件名为“Collection属性所在类的全名 -conversion.properties”,文件位置: Collection属性所在类的类路径下,文件格式:Element_Collection属性名=Collection中元素类全名
c.在Collection所在类上标注Annotation:
@Conversion(conversions={ @TypeConversion(rule=ConversionRule.ELEMENT,key = "Element_Collection属性名", converter= "Collection中元素类的全名") } )d. 在Collection属性的get/set方法上标注 TypeConversion Annotation
@TypeConversion(rule=ConversionRule.ELEMENT,key = "Element_Collection属性名", converter= "Collection中元素类的全名")
如果给定值与元素中的主键值对比过,没有匹配的,如何新建这样的一元素??
1.首先需要允许新建对象ReflectionContextState.isCreatingNullObjects(context)==true
这个在哪设定,得看完拦截器源码再写。
2.配置了类中Collection中元素允许新建,有如下几种配置方式:
a.Collection属性、Collection属性的set/get方法上标注的CreateIfNullannotation
b..配置properties文件,文件名为“Collection属性所在类的全名 -conversion.properties”,文件位置: Collection属性所在类的类路径下,文件格式:CreateIfNull_Collection属性名=true
c.在Collection属性所在的类上标注Annotation:
@Conversion(conversions={ @TypeConversion(rule=ConversionRule.CREATE_IF_NULL,key = "CreateIfNull__Collection属性名", value= "true") } )d.在Collection属性的get/set方法上标注TypeConversion Annotation
@TypeConversion(rule=ConversionRule.CREATE_IF_NULL,key = "CreateIfNull__Collection属性名", value= "true")
Collection 对象,表达式可用属性:size、iterator、isEmpty
五、XWorkListPropertyAccessor
它扩展了ListPropertyAccessor,
从List获取对象(getProperty):
如果ReflectionContextState.isGettingByKeyProperty(context)==true,即根据List中元素的主键属性获取元素,则交给XWorkCollectionPropertyAccessor处理。
其它情况直接交给父类ListPropertyAccessor获取,返回值不为空则直接返回。
返回值为空则且以索引号方式取值,且配置了取值==null时,新建对象时,创建新对象并存入List,再返回。
如何配置取值为空时,新建对象?见上面XWorkCollectionPropertyAccessor说明的前提部分
List 对象,表达式可用属性:size、iterator、isEmpty|empty、list对象属性名[‘索引号’]
六、XWorkMapPropertyAccessor
它扩展了MapPropertyAccessor,
从Map获取对象(getProperty):
委托父类MapPropertyAccessor获取值,如果取值为null且以Key值获取值方式,则把key转换为Map中Key元素的实际类型,再调用Map.get(key元素的正确类型),前提是需要配置key元素的类型,有几中配置方式:
a.Map属性、Map属性的set/get方法上标注的Key annotation
b.配置properties文件,文件名为“Map属性所在类的全名 -conversion.properties”,文件位置: Map属性所在类的类路径下,文件格式:Key_Map属性名=Map中Key元素的实际类型全名
c.在Map所在类上标注Annotation:
@Conversion(conversions={ @TypeConversion(rule=ConversionRule.KEY,key = "Key__Map属性名", converter="Map中key元素类型的全名") } )
d.在Map属性的get/set方法上标注
TypeConversion Annotation
@TypeConversion(rule=ConversionRule.KEY,key = "Key__Map属性名", converter="Map中key元素类型的全名")
Map
对象,表达式可用属性:
size、
keys|keySet
、
isEmpt、
values、
Map对象属性名.key值