8.1 OGNL语法
8.1.1 ActionContext、StackContext、ValueStack关系
ActionContext、StackContext、ValueStack对象与OGNL密切相关,理解三者之间的关系是掌握OGNL语法的根本。
1. ActionContext对象
ActionContext(com.opensymphony.xwork.ActionContext)是Action执行时的上下文,上下文可以看作是一个容器,它存放Action在执行时需要用到的对象,例如,请求的参数(Parameter)、会话(Session)、Servlet上下文(ServletContext)。在每次请求执行Action之前都会创建新的ActionContext。ActionContext是线程安全的,在同一个线程中ActionContext的属性是唯一的。值得注意的是ActionContext不仅保存了Action在执行时需要用到的数据,本身也可存放数据,例如以下代码用于将uid变量值保存到ActionContext对象中:
ActionContext.getContext().put("uid", "admin");
保存到ActionContext对象中的数据以后可通过Struts2标签和EL表达式语言进行访问。
2. StackContext 对象
Stack Context对象是ActionContext上下文中的一个java.util.Stack 实例。上述提及的请求参数(Parameter)、会话(Session)、Servlet上下文(ServletContext)等对象,实质是存储在ActionContext上下文中的StackContext对象中。
3. ValueStack对象
Struts2会为每一次Action的请求创建与之对应的ValueStack,并将所有Action属性数据存储到ValueStack中。再将ValueStack暴露给视图页面,这样页面就可直接访问后台处理生成的数据。
ActionContext、StackContext、ValueStack三者之间的关系如图1.8.1所示:
图 1.8.1 ActionContext、StackContext、ValueStack关系
假设存在LoginAction控制器,代码如下:
public class LoginAction
{
private String uid;//封装用户名
private String pwd;//封装密码
public String execute() throws Exception
{
//获取ActionContext对象
ActionContext ctx=ActionContext.getContext();
//将uid的值存储到session中
ctx.getSession().put("uid", this.uid);
//将uid的值存储到Stack Context中
ctx.put("uid", this.uid);
return "success";
}
//getter、setter代码省略…
}
并且已经正确将该Action的uid属性赋值为scott,pwd属性赋值为tiger。则ActionContext、StackContext、ValueStack三者此时的数据存放情况如图1.8.2所示:
图1.8.2 LoginAction数据存储情况
8.1.2 OGNL语法基础
OGNL是Object-Graph Navigation Language(对象图导航语言)的缩写,它是一种功能强大的表达式语言,通过它的简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。
1.访问OGNL上下文中的数据
ActionContext是Struts2中OGNL的上下文环境,也即向ActionContext中添加的数据(实际存储在Stack Context中)都可以被OGNL来访问,而ActionContext又包括request,session,ValueStack等这些对象,所以OGNL也可以访问存储在这些对象中的数据。
Value Stack(值栈)是OGNL的根,由于OGNL上下文中的根对象可以直接访问,所以通过OGNL访问Value Stack中的数据时不需要使用任何特殊的“标记”,而访问ActionConext中的其他对象则需要使用“#”标记。在Struts2中提供了<s:property>标签来访问OGNL上下文中的数据。
(1) <s:property value=”uid”/>
运行该标签时,Struts2会在Value Stack中去查找名为uid项,并输出其值
(2) <s:property value=”#uid”/>
运行该标签时,Struts2会在StackContext中去查找名为uid项,并输出其值
(3) <s:property value=”#session.uid”/>
运行该标签时,Struts2会在StackContext中的session作用域中去查找名为uid项,并输出其值。
(4) <s:property value=”#attr.uid”/>
运行该标签时,Struts2会依次在StackContext的page,request,session,application范围内查找对应的uid中的值。
2.访问JavaBean属性
假设存在employee对象作为OGNL上下文的根对象,对于下面的表达式:
(1) <s:property value=”name”/>
运行该标签时,Struts2会执行employee对象的getName()方法,对应的Java代码是:employee.getName()
(2) <s:property value=”address.country”/>
运行该标签时,Struts2会执行employee对象的getAddress()方法和getCountry()方法。对应的Java代码是:employee.getAddress().getCountry()
3.执行JavaBean方法
OGNL不仅可以自动执行getter方法,还能执行JavaBean的其它方法,更重要的是,在执行方法时,还能给方法传入参数。OGNL执行的方法或以静态方法或非静态方法。
执行静态方法的格式为:@类的全限名@方法名(参数),为了能让OGNL执行静态方法,必须在struts.xml文件中配置常量,以开启调用静态方法功能,配置代码如下:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
以下示例演示了使用OGNL执行JavaBean方法:
(1) 创建一个JavaBean,命名为OgnlInfo.java,代码如下:
public class OgnlInfo {
//定义静态方法
public static String sayBye(String name)
{
return name+"再见!";
}
//定义非静态方法
public String sayHello(String name)
{
return name+"你好!";
}
}
(2) 创建index.jsp页面,代码如下:
<body>
<%
//实例化ognl对象
OgnlInfo ognlInfo=new OgnlInfo();
//将ognlInfo对象存入request作用域内
request.setAttribute("ognlInfo",ognlInfo);
%>
<!-- 执行ognl对象的非静态方法 -->
<s:property value="#request.ognlInfo.sayHello('jack')"/>
<!-- 执行ognl对象的静态方法 -->
<s:property value="@com.soft.struts8l.entity.OgnlInfo@sayBye('tom')"/>
</body>
运行index.jsp页面,结果如图1.8.3所示:
图1.8.3 OGNL执行JavaBean的方法
4.OGNL操作符号
OGNL表达式中能使用的操作符基本跟Java里的操作符一样,除了能使用 +, -, *, /, ==, !=, = 等操作符之外,还能使用 mod(取余), in, not in等
5. %符号
%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值。以下示例代码用于演示%符号在OGNL中的使用方法。
<body>
<%
String title="提交";
pageContext.setAttribute("title",title);
%>
不使用%符号,会按原样显示
<s:submit value="#attr.title"/><hr>
使用%符号,会计算OGNL表达式的值
<s:submit value="%{#attr.title}"/>
</body>
运行此示例,结果如图1.8.4 所示:
图1.8.4 OGNL %符号的用法
6. OGNL中的集合操作
(1) 生成List类型集合
{e1,e2,e3 . . . }
以上语法,直接生成一个List类型集合,该集合包含了3个元素:e1、e2和e3。如果需要更多元素,则多个元素之间以英文逗号隔开。
(2) 生成Map类型集合
#{key1:value1,key2:value2,. . . }
以上语法,直接生成一个Map类型集合,该Map中每个key-value对象之间以英文冒号隔开;多项之间以英文逗号隔开。
(3) 使用in 和 not in 运算符
对于集合,OGNL提供两上元素符:in和not in,其中in判断某个元素是否在指定集合中;not in则用于判断某个元素是否不在指定集合中。以下代码演示了in和not in的用法:
<!-- 判断java是否在指定集合中 -->
<s:if test=" 'java' in {'java','jsp','html'}">
在里面
<s:else>
不在里面
</s:else>
</s:if>
(4) 获取集合的子集
OGNL允许通过某个规则取得集合的子集。取得子集时有以下3种操作符:
?:取出所有符合选择逻辑的元素
^:取出符合选择逻辑的第一个元素
$:取出符合选择逻辑的最后一个元素
假定在session作用域中存在名为persons集合, 该集合由多个Person类的实例组成, 在Person类中存在一个age属性,用于描述年龄,则#session.persons.{? #this.age>20} 表达式用于取出年龄大于20的persons类子集合;#session.persons.{? ^this.age>20} 表达式用于取出第一个年龄大于20的Person类实例;#session.persons.{? ^this.age>20} 表达式用于取出最后一个年龄大于20的Person类实例。