这里,我们将介绍一些和Struts2框架并存的ONGL的特性的例子。
Struts2框架使用一个标准的命名上下文来计算OGNL表达式。处理OGNL的顶级对象是一个
Map。ONGL有一个概念:上下文环境中只有一个根对象。在表达式中,根对象的属性不用任何
特殊的"标记"概念就可以引用。引用其他的对象需要使用#标识符。
Struts2框架将ActionContext设置为OGNL的上下文,并将值栈(ValueStack)设置为OGNL的
根对象。同根对象一起,Struts2框架也将其他对象放在了ActionContext中,包括代表application,
session,和request上下文的Map对象。这些对象和值栈在ActionContext中共存。
|
|--application
|
|--session
context map---|
|--value stack(root)
|
|--request
|
|--parameters
|
|--attr (searches page, request, session, then application scopes)
| |
在上下文map中,还有其他对象。上图只是个示例
请求的Action实例通常被push到值栈中。因为Action在值栈中,并且值栈是OGNL的根对象,引用
Action的属性可以省去#标识符。但是,要访问ActionContext中的其他非根对象,则必须加上#。这样
OGNL才知道不是在根对象中查找,而是在ActionContext中的其他对象中查找。
引用Action的属性 |
<s:property value="postalCode"/> //因为Action对象在值栈(ONGL根)的顶部 |
访问ActionContext中的非根对象可以使用#
<s:property value="#session.mySessionPropKey"/> |
<s:property value="#sessiob['mySessionPropKey']"/> |
<s:property value="#request['myRequestPropKey']"/> |
ActionContext通过静态方法也曝露给了Action类
ActionContext.getContext().getSession().put("mySessionPropKey", mySessionObject); |
还可以将表达式放在不支持动态内容的属性中,如下
<c:set var="foo" value="bar" scope="request"/> //JSTL标签 <s:textfield name="username" label="%{#request.foo}"/> |
集合(Maps,Lists,Sets)
在Struts2框架中经常会处理集合,下面有几个使用select标签的例子。
创建List的语法:{e1, e2, e3}。该语法创建一个包含字符串"name1","name2","name3"的list。
同时选择"name2"作为默认值。
<s:select label="label" name="name" list="{'name1','name2','name3'}" value="%{'name2'}" /> |
创建map的语法:#{key1:value1,key2:value2}
<s:select label="label" name="name" list="#{'foo':'foovalue','bar':'barvalue'}" /> |
使用in和not in来判断一个元素是否在集合中
<s:if test="'foo' in {'foo','bar'}" > mubahaha </s:if> <s:else> boo </s:else> <s:if test="'foo' not in {'foo','bar'}" > mubahaha </s:if> <s:else> boo </s:else> |
标签语法
标签是定制来显示动态数据的。为了创建一个显示属性"postalCode"的输入域,我们将字符串
"postalCode"传给textfield标签
<s:textfield name="postalCode"/> |
如果在值栈中有一个 "postalCode"属性,那么该属性的值将被传递到输入域中。当输入域被提交
回框架的时候,控件的值会被设置回"postalCode"属性。
有时,我们想向一个标签传递动态数据。比如,我们想在一个输入域中显示label,而我们也可能
想从应用的信息资源中获得这个label。相应地,框架会解析在标签属性中找到的表达式,这样我们便
可以在运行时将动态数据添加到标签属性中。表达式的转译符号是%{}。任何嵌入在该转译符号中的文本
都会被当成表达式来计算。
<s:textfield key="postalCode.label" name="postalCode"/> //key表示取国际化资源 |
表达式语言使得我们可以调用方法和求属性值。getText方法由ActionSupport类提供,该类是大多数
Action类的基类。因为Action在值栈中,我们可以通过表达式调用它的任何方法,包括getText。
非String属性
HTTP协议是基于文本的,但有些标签有非String类型的属性,像bool或int等。为了方便使用非String
属性,框架将所有非String属性作为表达式来求值。在这种情况下,不必使用转译标识符。但如果你这么
做了(即加了转译标识符),框架会去掉它。
<s:select key="state.lebel" name="state" multiple="true"/> |
由于属性multiple映射到一个boolean属性,框架不会将它解释成一个String类型的值。该值
作为表达式求解并自动转换为一个boolean类型。
由于很容易忘掉哪些属性是String,哪些不是String,那你可以都加上转译标识符%{}。
value是一个对象
由于name属性告诉框架调用哪个属性来为value赋值,通常情况下,value属性是自动赋值的。
但如果有原因要为value直接赋值,注意:value是一个对象而不是一个String。由于value不是一个
String,无论传递什么给value都会被当做一个表达式——而不是一个String字面量。
面的标签很可能错了 |
<s:textfield key="state.label" name="state" value="ca"/> |
如果传递值属性"ca"给textfield,框架会寻找一个叫getCa的属性。一般情况下,这不是我们想做的。
我们打算做的是传递一个字面量String。在表达式语言中,字面量放在引号中。
正确传递字面量的方法 |
<s:textfield key="state.label" name="state" value="%{ 'ca' }"/> |
另一种方式是使用语法 value=" 'ca' " ,但在这种情况下,建议使用表达式记号。
总结一下,求标签属性的值要使用三条规则:
1、所有的String属性类型被解析为"%{...}"符号。
2、所有非String属性类型不被解析,而是当做表达式直接求值。
3、规则2的例外情况是:如果非String属性使用了%{}转译符号,那么该记号由于是多余的而被忽略,
而对记号里面的内容求值。