OGNL表达式语言介绍
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
OGNL相对其它表达式语言具有下面几大优势:
1. 支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或@tutorial.MyConstant@APP_NAME;</pre>
3、支持赋值操作和表达式串联,如price=100, discount=0.8,
calculatePrice(),这个表达式会返回80;
4、访问OGNL上下文(OGNL context)和ActionContext;
5、操作集合对象。
Ognl 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了Java.utils.Map的接口.
理解Struts2中的 ValueStack:
ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。
ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个
ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前
Action 对象和其他相关对象.
Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中,request中。
在 ValueStack 对象的内部有两个逻辑部分:
ObjectStack: Struts 把Action和相关对象压入 ObjectStack 中--List
ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中
Struts 会把下面这些映射压入 ContextMap 中
parameters: 该 Map 中包含当前请求的请求参数
request: 该 Map 中包含当前 request 对象中的所有属性
session: 该 Map 中包含当前 session 对象中的所有属性
application:该 Map 中包含当前 application 对象中的所有属性
attr: 该 Map 按如下顺序来检索某个属性: request, session, application
理解OGNL Context:
OgnlValueStack 类包含两个重要的属性 一个root(对象栈)和一个context(Map栈)。
* 其中root(对象栈,即CompundRoot类型)本质上是一个ArrayList.
里边保存各种和当前 Action 实例相关的对象.
且默认将当前Action对象压入栈顶,如果action中没有手动将任何对象压入栈顶,
那么栈顶对象一直是当前Action对象。
* 而context 是一个Map(更确切的说是一个OgnlContext对象)
也是对 ActionContext 的一个引用. 里边保存着各种 Map:
requestMap, sessionMap, applicationMap, parametersMap, attr
在这个OgnlContext对象(context)中,有一个默认的顶层对象 \_root,OGNL访问context中这个默认顶层对象中的元素时,是不需要#号的,直接通过元素的名称来进行访问,
而访问其他对象时,如 request、session、attr等,则需要#号引用。
注:Struts2将OgnlValueStack的root对象赋值给了OgnlContext 中的_root对象,
在OgnlValueStack的root对象中,保存着调用Action的实例,因此,在页面上通过Struts2标签访问Action 的属性时,就不需要通过#号来引用
总结:ognl Context包含 ObjectStack属性和ContextMap属性
当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。
注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value="name"/>
读取值栈中的属性值
- 访问对象属性不需要#号,访问Map中的属性需要加#号
- 对于对象栈: 对象栈中某一个对象的属性值
- 若想访问 Object Stack 里的某个对象的属性. 可以使用以下几种形式之一:
- object.propertyName ; object['propertyName'] ; object["propertyName"]
- ObjectStack 里的对象可以通过一个从零开始的下标来引用. ObjectStack 里的栈顶对象可以用 [0] 来引用,
它下面的那个对象可以用 [1] 引用. [0].message:表示取第一个对象的message属性值 - [n]的含义是从第n个对象开始搜索,但不限于第n个,如果第n个中没有,会到n+1个对象中找,依次类推下去,如果所有对象中都没找到,那就真的没有了。
- 若从栈顶对象开始搜索, 则可以省略下标部分: message
- 结合 s:property 标签: <s:property value="[0].message" /> 等价于 <s:property value="message" />
- 默认情况下, Action 对象会被 Struts2 自动的放到值栈的栈顶.
- 若想访问 Object Stack 里的某个对象的属性. 可以使用以下几种形式之一:
- Map 栈: request, session, application 的一个属性值 或 一个请求参数的值.
- 使用<s:property value=""/>标签取值,如:
ActionContext.getContext().put("myTest","HelloWorld");
<s:property value="#request.myTest"/>
取request中myTest这个key对应的value。
ActionContext.getContext().put("test",test);
<s:property value="#request.test.productName"/>
EL方式取:${requestScope.test.productName}
取request中test这个key对应test对象的productName属性值。
注:其中#requet也可以不写,<s:property value="#test.productName"/>
- 使用<s:property value=""/>标签取值,如:
取值示例
区分是对象站还是Map栈看使用的方法时push(Object)还是put(String,Object),前者是放到对象栈,后者放到Map栈。
/**测试Map栈*/
public String testBean() {
ActionContext context = ActionContext.getContext();
Ognl ognl = new Ognl();
ognl.setName("zhangsan");
ognl.setDescription("是个好人");
context.put("singleBean", ognl);
context.put("singleBean2", "testAction2");
// <s:Property value="#request.singleBean3">
// ServletActionContext.getRequest()
// .setAttribute("singleBean3","testAction3");
return "success";
}
/**测试对象栈*/
public String testObjectStack() {
// 把一个对象放入到栈顶
Ognl ognl = new Ognl();
ognl.setName("lisi");
ActionContext.getContext().getValueStack().push(ognl);
// 或者
ActionContext.getContext().getValueStack().getRoot().add(0, ognl);
// 获取栈顶元素
ActionContext.getContext().getValueStack().peek();
// 或者
ActionContext.getContext().getValueStack().getRoot().get(0);
// 弹栈
ActionContext.getContext().getValueStack().pop();
// 改变对象栈中的某个元素的值
ActionContext.getContext().getValueStack().setValue("name", "aaa");
ActionContext.getContext().put("bbb", "bbbb");
String name = ActionContext.getContext().getValueStack()
.findValue("name").toString();
String bbb = ActionContext.getContext().getValueStack().findValue("bbb")
.toString();
System.out.println(name);
System.out.println(bbb);
return "success";
}
<!-- EL获取key为singleBean对象的name属性 -->
actionContext.put : ${requestScope.singleBean.name} <br>
<!-- EL获取key为singleBean2的value值 -->
actionContext.put2 : ${requestScope.singleBean2} <br>
<!-- OGNL获取key为singleBean对象的name属性 -->
actionContext.put3 : <s:property value="#request.singleBean.name"/><br>
<!-- OGNL获取key为singleBean2的value值 -->
actionContext.put4 : <s:property value="singleBean2"/><br>
/**测试迭代List*/
public String testList() {
List<Ognl> list = new ArrayList();
Ognl ognl = new Ognl();
ognl.setName("testList1");
ognl.setDescription("testList111");
list.add(ognl);
Ognl ognl2 = new Ognl();
ognl.setName("testList2");
ognl.setDescription("testList222");
list.add(ognl2);
ActionContext.getContext().put("testList", list);
return "success";
}
<!-- 迭代 List -->
<s:iterator value="#testList">
<!-- 直接用对象属性取值 -->
<s:property value="name"/>
<s:property value="description"/>
</s:iterator>
/**测试迭代Map*/
public String testMap() {
Map<String, Ognl> map = new HashMap();
Ognl ognl = new Ognl();
ognl.setName("开发部");
ognl.setDescription("程序员比较多");
map.put("a", ognl);
Ognl ognl2 = new Ognl();
ognl.setName("UI部");
ognl.setDescription("女生比较多");
map.put("b", ognl2);
ActionContext.getContext().put("testMap", map);
return "success";
}
<!-- 迭代 Map<String,Ognl> -->
<s:iterator value="#testMap">
<s:property value="key"/>
<!-- 迭代Map其实就是迭代Map中的Entry。
Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。
它表示Map中的一个实体(一个key-value对)。接口中有getKey(),getValue方法。
上面的value="key" -> entry.getKey()。
下面的value=”value.name" -> entry.getValue().name
-->
<s:property value="value.name"/>
</s:iterator>
/**测试listMap*/
public String testListMap() {
List<Map<String, Ognl>> listMap = new ArrayList();
Map<String, Ognl> map = new HashMap();
Ognl ognl = new Ognl();
ognl.setName("开发部");
ognl.setDescription("程序员比较多");
map.put("a", ognl);
Ognl ognl2 = new Ognl();
ognl.setName("UI部");
ognl.setDescription("女生比较多");
map.put("b", ognl2);
listMap.add(map);
ActionContext.getContext().put("testListMap", listMap);
return "success";
}
<!-- 迭代List<Map<String,Ognl>> -->
<s:iterator value="#testListMap"><!-- 第一个是迭代List -->
<s:iterator><!-- 第二个是迭代Map -->
<s:property value="key"/>
<s:property value="value"/>
</s:iterator>
</s:iterator>
/**测试mapList*/
public String testMapList() {
Map<String, List<Ognl>> mapList = new HashMap();
List<Ognl> list = new ArrayList();
Ognl ognl = new Ognl();
ognl.setName("testList1");
ognl.setDescription("testList111");
list.add(ognl);
Ognl ognl2 = new Ognl();
ognl.setName("testList2");
ognl.setDescription("testList222");
list.add(ognl2);
mapList.put("ml", list);
ActionContext.getContext().put("testMapList", mapList);
return "success";
}
<!-- 迭代Map<String,List<Ognl>> -->
<s:iterator value="#testMapList">
<s:property value="key"/>
<s:iterator value="value">
<s:property value="name"/>
<s:property value="description"/>
</s:iterator>
</s:iterator>
/**测试listMapList*/
public String testListMapList() {
List<Map<String, List<Ognl>>> listMapList = new ArrayList();
Map<String, List<Ognl>> mapList = new HashMap();
List<Ognl> list = new ArrayList();
Ognl ognl = new Ognl();
ognl.setName("testList1");
ognl.setDescription("testList111");
list.add(ognl);
Ognl ognl2 = new Ognl();
ognl.setName("testList2");
ognl.setDescription("testList222");
list.add(ognl2);
mapList.put("ml", list);
listMapList.add(mapList);
ActionContext.getContext().put("testListMapList", listMapList);
return "success";
}
<!-- 迭代List<Map<String,List<Ognl>>> -->
<s:iterator value="#testListMapList"><!-- 最外层list -->
<s:iterator><!-- map -->
<s:property value="key"/>
<s:iterator value="value"><!-- map中的list -->
<s:property value="name"/>
<s:property value="description"/>
</s:iterator>
</s:iterator>
</s:iterator>
取值原则:当前栈顶对象是什么,
如果是list,那么就是list中的第一个元素。
如果是map,那么就是第一个entry。
value="key" : 取出map 中 key 的值
value="value" : 取出map中 key所对应的value值
网友评论