OGNL表达式,值栈(ValueStack),类型转换
1. 什么是OGNL?
l 全称是Object Graphic Navigation Language,对象图导航语言
它是一种强大的表达式语言,可以通过简单一致的表达式语法来读取和设置Java对象的属性值、调用对象的方法、遍历整个对象的结果图、实现字段类型转换等功能,它是一个开源项目。
l OGNL表达式的计算是围绕OGNL上下文来进行的
l OGNL上下文实际就是个Map对象,由ognl.OgnlContext类(实现了java.util.Map接口)
l OGNL上下文包含多个JavaBean对象,其中包含一个特殊对象,称为根对象(root)
l 在写表达式时,若没指定使用上下文中的某个对象,则根对象作为表达式所依据的对象
l 在写表达式时,访问根对象的属性property就直接书写,否则需要使用#key作为前缀,如:#javabean.property
l OGNL表达式中的属性(property)是JavaBean的属性(setter/getter),而不是类中的实例变量;在访问JavaBean属性实质就是调用相关的getter方法
2. OGNL支持的常量类型如下
l 字符串常量:使用单引号或双引号括起来的字符串,如:"Hello World! “,‘Hello'
若使用单个字符作为字符串常量,则需要使用双引号括起来如: “H"
l 字符常量:使用单引号括起来的单个字符,不能使用双引号,如:‘H'
l 数值常量: 除Java中的int、long、float和double外,还可以使用b或B后缀指定BigDecimal常量:使用h或H后缀指定BigInteger常量
如:12(int常量),12l(long常量),12.34f(float常量),12.34(double常量),12b(BigDecimal常 量),12h(BigInteger常量)
l 布尔常量:true和false
l null常量
3. OGNL特有的操作符
l 逗号(,):用于分隔两个或多个独立的表达式,整个表达式是最后一个表达式的值
l 大括号({ }):用于创建列表,创建时将元素使用大括号括起来,元素之间使用逗号分隔。 例如:{"zhangsan","lisi","wangwu"}
l in和not in:用于判断一个值是否在集合中
4. 创建Map
语法:{"key1":"value1","key2":"value2","key3":"value3"}
例如:{"one":"zhangsan","two":"lisi","three":"lisi"}
可以创建指定的Map类型,例如
#@java.util.LinkedHashMap{"one":"zhangsan","two":"lisi","three":"lisi"}
Map对象通过key来进行访问
例如:map[“one”]或者map.one或者
{"one":"zhangsan","two":"lisi","three":"lisi"}[‘one']
5. 集合的伪属性
OGNL为了简化对Java集合API的方法调用,OGNL提供了一些伪属性,可以按照属性的
方式来访问集合中的方法。
List,Set,Map | size,isEmpty | List.size,set.isEmpty | list.size()、set.isEmpty() |
List | iterator | list.iterator | list.iterator() |
Map | keys、values | Map.keys,map.values | map.keySet()、map.values() |
Set | iterator | set.iterator | set.iterator() |
Iterator | next、hasNext | Iter.next,iter.hasNext | iter.next()、iter.hasNext() |
Enumeration | Next,hasNext, nextElement, hasMoreElements | Enum.next, enum.hasNext | enum.nextElement() enum.hasMoreElements() |
6.投影
OGNL提供了一种简单方式在一个集合上对每个元素调用相同的方法,并将其结果保存为一个新的集合
例如:
persons是一个包含了person对象的列表,#persons.{name}将返回所有人的名字
投影期间可以使用#this表示当前元素的引用
objects.{ #this instanceof java.lang.String ? #this: #this.toString() }
表示将对象列表中的元素作为字符串值产生一个新的元素列表
7.选择
OGNL提供了一种简单的方式来使用表达式从集合中选择某些元素,并将其结果保存到新的集合中
l #persons.{?#this.age> 20}
persons表示包含了person对象的列表,返回年龄大于20的所有用户
l #persons.{?#this.age> 20}[0]
表示返回年龄大于20的所有用户中的第一个用户
**** 需要注意防止发生ArrayIndexOutOfBoundsException异常
u ?:表示选取匹配逻辑的所有元素
例如,#persons.{?#this.age > 20}
persons表示包含了person对象的列表,返回年龄大于20的所有用户
u ^:表示选取匹配选择逻辑的第一个元素
例如, #persons.{^#this.age > 20}
persons表示包含了person对象的列表,返回年龄大于20的所有用户中的第一个
u $:表示选取匹配选择逻辑的最后一个元素
例如, #persons.{$#this.age > 20}
persons表示包含了person对象的列表,返回年龄大于20的所有用户中的最后一个
8. lambda表达式
OGNL提供了一个简化的lambda表达式语法,定义lambda表达式的语法是:[...],其中lambda表达式只能使用一个参数,该参数可以通过#this来进行引用。OGNL将lambda表达式看做是常量
例如,声明一个计算递归的函数,并调用它
#fact = :[#this<=1?1:#this*#fact(#this-1)] ,#fact(30H)
#this表示表达式的参数
初始值为30H,其中H表示BigInteger,每次调用递归表达式都将参数减1
9. 值栈(ValueStack)
l Struts2将OGNL上下文设置为Struts2中的ActionContext,并将值栈作为OGNL的根对象
l 值栈类似于正常的栈,遵循后进先出的栈特点
l 可以在值栈中存入、删除和查询对象,它是Struts2的核心
l 值栈是通过接口(com.opensymphony.xwork2.util.ValueStack)来定义,对应的实现类是com.opensymphony.xwork2.ognl.OgnlValueStack
l ActionContext对象中还放置了其他对象,包括表示application、session和request的Map对象。这些对象共存于ActionContext中,靠在值栈(OGNL根对象)的旁边
l 访问值栈
1) OGNL上下文中的值栈对象可以直接访问,但引用上下文中其他对象需要使用井号(#)来标记
2) 在Struts2中,可以自动从栈顶到栈底查找栈内所有的对象,直到查找到一个具有指定查找属性的对象,即,值栈中的任意对象都可以直接访问,而不需要使用井号(#)例如:有Person和Student对象,都具有name属性,先往栈里存入Person对象,再存入Student对象, Student对象位于栈顶,那对于表达式name,则表示访问Student中的name属性,因为Student位于栈顶,先找到Student对象
3)
10. [N]语法
l 若需要访问Person对象的name属性,可以使用[N].xxx语法来指定从哪个位值向下开始查找对象的属性;其中,N表示从0开始的整数,它表示从栈中第N个位值开始查询具有xxx属性的对象
例如,表达式[1].name:表示从值栈中索引为1的位值开始查找,查找具有name 属性的对象,并将name输出
11. top关键字
l 在Struts2中,top关键字用于获取栈顶的对象
l [0].top:表示访问Object0对象
l [1].top:表示访问Object1对象
l [2].top.name:表示访问Object2对象的name属性
12. 访问静态成员
l 除了使用标准的OGNL表达式访问静态字段和属性外,Struts2还允许使用“vs”前缀来调用保存在栈中的对象和静态字段和静态方法,而且不需要指定完整的类名
例如: @vs@someProperty
@vs@someMethod()
@vs2@someProperty
@vs2@someMethod()
l 说明: 其中vs表示“value stack”
直接使用vs表示栈顶的对象
若vs后面若有数字,则表示将使用栈中指定位值处的对象的类
13. 值栈中的Action实例
l Struts2框架总是把Action实例放置在栈顶;由于Action在值栈中,而值栈又是OGNL的根,所以在引用Action属性时可以省略“#”标记
例如:<s:property value=“xxx"/>
表示输出栈顶的Action实例的xxx属性,但访问其他对象时,需要使用“#”标记
14. Struts2中的命名对象
l Struts2中还提供了一些命名对象,这些对象没有保存在值栈中,而是保存在ActionContext中,访问这些对象需要使用“#”标记,这些对象都是Map类型
l 命名对象有以下几类
1) parameters
2) request
3) session
4) application
5) attr
l parameters
用于访问请求参数。例如,#parameters[‘pid’]或parameters.pid,相当于调用 HttpRequestServlet对象的getParameter()方法
l request
用于访问请求属性。例如,#request[‘user’]或#request.‘user’,相当于调用 HttpRequestServlet对象的getAttribute()方法
l session
用于访问session属性。例如, #session[‘user’]或#session.‘user’,相当于调用 HttpSession 对象的getAttribute()方法
l application
用于访问application属性。例如, #application[‘user’]或#application.‘user’,相 当于调用ServletContext对象的getAttribute()方法
l attr
如果PageContext可用,则访问PageContext,否则将依次搜索request、session 和 application,例如,若PageContext对象不可用,#attr[‘user’]或#attr.‘user’ 将依次搜索request、session和application对象
14. 与JSP的冲突
在JSP2.1规范中,井号#已经被JSP中的EL表达式语言所使用
对于某些使用OGNL#操作符的应用程序,会出现一些问题,为了避免该问题的出现,通常在web.xml文件中添加代码,禁用JSP中EL表达式的使用
如下所示
15. 类型转换
l Java是强类型语言,而MVC框架从用户接受过来的数据均是字符串,那么MVC框架就需要将请求的字符串转换成相应的数据类型
l Struts2提供了非常强大的类型转换支持
l Struts2类型转换机制的基础是OGNL表达式
l 可以使用ONGL表达式来命名参数
例如,Action类有user属性(类型为User,并有相应的setter和getter方法),那在 表单页面相应的使用OGNL表达式命名,如user.name、user.age等
16. 对集合的支持
l 在Action中,将相关属性的泛型取消
l 在对应的Action目录下创建名为<Action类>-conversion.properties的文件
l 在该文件下指定集合元素的数据类型
1) 如果集合是java.util.List,格式为:Element_xxx=复合类型
其中Element是固定的,xxx表示Action中属性的集合属性名,复合类型是集合元素 类型的完全限定名
2) 如果集合是java.util.Map,格式为:Key_xxx=复合类型
其中Key是固定的,xxx表示Action中属性的集合属性名,复合类型是集合元素类型 的完全限定名
3) Set集合并不维护元素加入的顺序,也不能通过索引来访问元素
在Struts2提供了使用JavaBean对象的属性来索引集合元素方式,通过元素属性值 来唯一获取Set集合中的元素
用于索引集合元素的属性在<Action类名>-conversion.properties文件中通过 KeyProperty_xxx=yyy来指定
其中,xxx表示集合类型的属性,yyy表示是集合元素(即JavaBean对象)的属性名
说明:在BookSetAction类中必须要实例化books属性
第2行,指定集合对象
第3行,表示引用的集合元素为null时,框架会自动实例化该对象
17.自定义类型转换:
l 实现TypeConverter接口
l 继承自DefaultTypeConverter类 ****
l 继承自StrutsTypeConverter类 *****
l convertValue(Mapcontext,Object value,Class toType)方法
l context:表示OGNL上下文的Map对象
l value:表示需要转换的对象
l toType:表示需要转换的目标类型
l 配置自定义转换器
l 应用于全局范围的类型转换器
l 通常在WEB-INF/classes目录下创建xwork-conversion.properties文件,并提供相应的属性定义。其中,属性是要转换的类的名称,属性值是类型转换器的名称
例如:java.util.Date=xx.xx.XxxConverter
l 应用与特定类的转换器
配置针对某个特定类的类型转换器,方法是在类所在的包中创建 <ClassName>-conversion.properties文件,并提供一个属性定义,等号左边是要 转换的类的属性名,右侧是类型转换器的类名称