OGNL的全称是Object Graph Navigation Language(对象图形导航语言),它是一种强大的表达式语言,开发者可以通过简单一致的表达式语法来读取和设置java对象的属性值,调用对象的方法,遍历整个对象的结构。
操作对象!
OGNL有一个上下文(Context)概念,通俗的说就是一个Map结构,它实现了java.util.Map接口,在struts2中上下文(Context)的实现为ActionContext。即Struts2中OGNL Context实现者为ActionContext,它的结构示意图如下:
当Struts2接受一个请求时,会迅速创建ActionContext,然后创建ValueStack,再创建action。接着把action存放到ValueStack中,所以action中的实例变量可以被OGNL表达式直接访问。
OGNL表达式访问各个命名空间的属性:
1.访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#Session...
2.另外OGNL会设定一个根对象(root对象),在struts2中根对象就是ValueStack(值栈)。如果要访问根对象(即值栈ValueStack)中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。
3.在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是存放单个值,而是存放一组对象。在OgnlValueStack类里面有一个List类型的root变量,就是使用它来存放一组对象,如图:
4.在root变量中处于第一位的对象叫做栈顶对象。通常我们在OGNL表达式里直接写上熟悉你的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象开始寻找,依次往下访问,知道找到为止。
如:要访问存在栈顶中的一个name属性,我们不需要用到#命名空间的方式,可以直接使用对象属性名称的方式访问,即写个name即可,但是这样写又是无效的,在struts2中,OGNL表达式需要配合struts2标签才可以使用,应该写成这样:<s:property value="name">,这个name就是OGNL表达式。
5.访问值栈ValueStack中的对象,除了直接写属性名称以外,还可以使用EL表达式直接访问,如上面的name,可以这样访问:${name}。
注:EL表达式只限于访问值栈ValueStack中的对象的属性,如果要访问其他域的属性还得使用#命名空间的方式。
Application对象:用于访问ServletContext,例如:#application.userName或者#application['userName'](注【】的形式在一些特殊字符的时候使用,如#applicaiton['u-name']),这样相当于调用了ServletContext的getAttribute("username");
session对象:用来访问HttpSession,例如:#sesion.userName或者#session['userName'],相当于调用session.getAttribute("userName");
request对象:用来访问HttpServletRequest属性,例如:#request.userName或者#request['userName'],相当于调用request.getAttribute("userName");
parameters对象:用来访问Http的请求参数,例如:#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("userName");
attr对象:用于按page->request->session->application顺序访问属性。
在JSP中使用strus2的标签,必须要引入标签声明:
<%@taglib prefix="s" uri="/struts-tags" %>
1.set标签:用于将某个值放入指定的范围。
参数:
注:如果没有设置scope范围,那么默认为OGNL Context(访问时不需要命名空间,使用#即可)。
测试代码:
<body> <s:set name="age" value="22"></s:set> </body>
注:上述代码没有指定范围,即将age放入OGNL Context中,访问时使用#age即可。
2.property标签:用于输出指定的值:
value属性:可选,指定需要输出的属性值,如果没有该属性,则默认输出ValueStack栈顶的值(这个非常重要,有些时候不写value也可以输出,是因为要访问的属性在栈顶)。
测试代码1(访问request范围的属性):
<body> <% request.setAttribute("name", "习近平"); %> <!-- 访问request范围内的属性,要使用#命名空间的形式 --> 名称:<s:property value="#request.name"/> </body>
效果:
测试代码2(访问Session范围的属性):
<body> <% session.setAttribute("name", "李克强"); %> <!-- 访问session范围内的属性,要使用#命名空间的形式 --> 名称:<s:property value="#session.name"/> </body>
效果:
测试代码3(访问值栈中的属性):
/** * 在请求访问action的时候,系统会迅速创建ActionContext->valueStack->action * 并且把action存放在valueStack中,这就是为什么OGNL表达式可以直接以属性名称的方式访问属性。 * 因为action存放在了valueStack中。 * @author Liao * */ public class UserAction extends ActionSupport { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String execute(){ this.setName("胡锦涛"); return this.SUCCESS; } }
在界面访问:
<body> <!-- 访问valueStack中的属性,不需要使用#命名空间的形式,直接通过属性名的方式即可访问 --> 名称:<s:property value="name"/> <br> <!-- 值栈的值也可以直接通过EL表达式来访问 --> 通过EL表达式获取的名称:${name} </body>
效果:
3.Iterator迭代标签:用于遍历集合(java.util.Collection)或者枚举值(java.util.iterator)。
参数:
注:value为可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合。
在演示之前我们先看另外一个知识点:通过OGNL表达式来创建List/Map集合。
测试代码:
<body> <!-- 通过OGNL可以创建List和Map集合value="{'邓小平','胡锦涛','习近平'}"表示一个List集合 --> <s:set name="list" value="{'邓小平','胡锦涛','习近平'}"/> <!-- 上述集合没有指定范围就表示OGNL Context范围内的变量,使用#即可 ,iterator标签中的value表示要迭代的集合--> <s:iterator value="#list"> <!-- 注:Iterator标签在迭代集合时,会把当前迭代的对象放在值栈的栈顶,所以使用property标签输出时可以不用value --> <s:property /><br> </s:iterator> </body>
注:一定要记住,iterator标签在迭代集合时会把当前迭代的对象放在值栈的栈顶,这就是为什么使用property输出值时可以不写value属性的原因。
效果:
使用OGNL创建Map集合:
测试代码【通过OGNL可以创建List和Map集合value="#{'key1':'邓小平','key2':'胡锦涛','key3':'习近平'}"表示一个map集合(注意书写方式)】:
测试代码:
<body> <s:set name="maps" value="#{'key1':'邓小平','key2':'胡锦涛','key3':'习近平'}" /> <!-- 上述集合没有指定范围就表示OGNL Context范围内的变量,使用#即可 ,iterator标签中的value表示要迭代的集合--> <s:iterator value="#maps"> <!-- 注:Iterator标签在迭代集合时,会把当前迭代的对象放在值栈的栈顶,所以使用property标签输出时可以不用value --> <s:property value="key" />=<s:property value="value" /> </br> </s:iterator> </body>
效果:
OGNL表达式的投影功能,常用操作符:
1.?表示获得所有符合逻辑的元素。
2.^表示符合逻辑的第一个元素。
3.$表示符合逻辑的最后一个元素。
测试案例:
User对象:
public class User { private Integer uid; private String uname; private Integer age; public User() { } public User(Integer uid, String uname, Integer age) { this.uid = uid; this.uname = uname; this.age = age; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getUid() { return uid; } public void setUid(Integer uid) { this.uid = uid; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } }
UserAction.java:
/** * 在请求访问action的时候,系统会迅速创建ActionContext->valueStack->action * 并且把action存放在valueStack中,这就是为什么OGNL表达式可以直接以属性名称的方式访问属性。 * 因为action存放在了valueStack中。 * 这个类中的users属性可以直接通过属性名称的方式访问 * @author Liao * */ public class UserAction extends ActionSupport { private static final long serialVersionUID = 1L; private List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } public String execute(){ users = new ArrayList<User>(); users.add(new User(1, "习近平", 20)); users.add(new User(1, "胡锦涛", 30)); users.add(new User(1, "邓小平", 40)); return this.SUCCESS; } }
前台投影测试:
<body> <!-- users是action中的属性,即存放在ValueStack中,所以访问users不需要使用#命名空间的形式 --> <s:iterator value="users.{?#this.age > 30}"> <s:property value="uname" />:<s:property value="age" /> </br> </s:iterator> </body>
注:users是action中的属性,当然也是ValueStack中的对象,所以可以用属性名称的方式直接访问。在上述代码中,直接在集合后紧跟.{}运算符表明用于取出该集合的子集,{}的表达式用于获取符合条件的元素,this指的是为了从大集合users帅选数据到小集合,需要对大集合users进行迭代,this代表当前迭代的元素。
效果:
iterator标签中的status属性用来指定迭代时的IteratorStatus实例,该方法包含如下集合方法:
int getCount(),返回当前迭代了几个元素。
int getIndex(),返回当前迭代元素的索引。
boolean isEven(),返回当前被迭代元素的索引是否是偶数
boolean isOdd(),返回当前被迭代元素的索引是否是奇数
boolean isFirst(),返回当前被迭代元素是否是第一个元素。
boolean isLast(),返回当前被迭代元素是否是最后一个元素。
有了这些方法,我么可以做一些特殊效果:
测试代码:
<body> <s:set name="list" value="{'邓小平','胡锦涛','习近平'}" /> <s:iterator value="#list" status="s"> <!-- 如果是奇数就为红色,否则为蓝色 --> <font color=<s:if test="#s.odd">red</s:if> <s:else>blue</s:else>> <s:property/> </font> </s:iterator> </body>
效果:
4.if elseif else 标签:执行基本的条件流转。
参数:
测试代1:
<body> <s:set name="age" value="22"></s:set> <!-- 使用ognl接收值 --> <s:if test="#age > 20"> 您的青春已经让狗给吃了! </s:if> <s:elseif test="#age > 35"> 七年之痒,勿出轨! </s:elseif> <s:else> 青春年华,无限风光! </s:else> </body>
效果:
OGNL表达式可以使用in和not in两个元素符号,in表达式用来判断某个元素是否在指定的集合对象中,not in判断某个元素是否不在某个集合对象中。
测试代码2:
<body> <!-- OGNL表达式in的测试 --> <s:if test="'liao' in {'liao','zhong','min'}"> 您中大奖啦! </s:if> <s:else> 您被坑啦! </s:else> <!-- OGNL表达式not in的测试 --> <s:if test="'liao' not in {'lavimer','hello'}"> 你不在啊! </s:if> <s:else> 你在啊! </s:else> </body>
注:<s:select>下拉框、<s:checkboxlist>复选框、<s:radio>单选框在Web开发中经常使用,将在后面的文章中以专题的形式详细讨论。