1.OGNL是Object-Graph Navigation Language的缩写,是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。其中OGNL用得最多的地方就是和Struts2的标签绑定,也可以在配置文件中通过${}使用OGNL表达式。
2.OGNL表达式中有几个常用的符号,下面介绍这三种符号的使用:
(1).OGNL中$号的使用:第一种,可以在国际化资源文件中引用OGNL表达式。第二种,可以在struts.xml配置文件中引用OGNL表达式。
(2).OGNL中%号的使用:%符号的用途是在标志属性为字符串类型时,计算OGNL表达式的值。
~使用%{}可以取出保存在值堆栈中的Action对象,直接调用它的方法。
~如果Action类继承了ActionSupport类,那么在页面标签中可以使用%{getText(‘key‘)}获取国际化信息。
(3).OGNL中#号的使用:OGNL中的#号可以取出堆栈上下文中存放的对象,访问OGNL上下文和Action上下文。#相当于ActionContext.getContext()。
~其中在栈的上下文中有5个对象(attr,request,session,application,parameters):
attr:用于按request>>session>>application顺序访问其属性,#attr.userName,相当于按顺序从三个范围读取userName属性,直到找到为止。
request:包含当前HttpServletRequest的属性的Map,#request.userName,相当于request.getAttribute("userName")。
session:包含当前HttpSession的属性的Map,#session.userName,相当于session.getAttribute("userName")。
application:包含当前应用的ServletContext的属性的Map,#application.userName,相当于application.getAttribute("userName")。
parameters:包含当前HTTP请求参数的Map,#parameters.id[0],相当于request.getParameter("id")。
3.获取Action中的属性值或者Action中的对象的某某属性值:
利用<s:property/>标签可以直接获取Action中的引用类型user里面的userName属性,同样可以通过user.address.addr获取user中引用类型address中的addr属性的值,像这种一层层往下传递的访问方式,即所谓的导航,也就是一步步往下调用。
4.调用Action的对象里面的普通方法:
默认的会把Action放到值栈里面去,而值栈在访问的时候,并不需要值栈的名字,当我们使用<s:property value="user.getVOMethod()"/>的时候,它会自动到值栈里面查找Action对象里面有没有user对象,如果发现有user对象,然后它就再找user里面有没有getVOMethod()方法,如果发现有,于是调用getVOMethod()这个方法。
实际上调用getVOMethod()方法的过程与获取表单中的姓名密码的方式都是相同的,都是到值栈里面去查找,找是否存在user对象,如果存在,接着查找user中是否存在某某属性或方法。
5.调用Action中的静态方法:
我们可以在JSP页面中使用OGNL表达式调用Action中的静态方法,调用Action中的静态方法时,与上面调用user对象的getVOMethod()方法的过程,是截然不同的。此时value的写法是固定的,以@开头,后面跟上具体的包名,然后@加上静态方法,比如<s:property value="@[email protected]()"/>。另外user对象只是LoginAction中的一个属性,这个属性会自动放到值栈里面,而值栈调用的时候,不用加上@或者包名等等,所以直接user.getVOMethod()就可以了。
6.调用JDK类中的静态方法:
可以使用<s:property value="@@floor(46.68)"/>输出floor()的执行结果,这就意味着如果不在@@中指定类的话,默认就表示java.lang.Math类,当前大多数情况下,都不会省略这个类,都会写全了的,然后在后面加上静态方法。
7.集合的伪属性:OGNL能够引用集合的一些特殊的属性,这些属性并不是JavaBean模式,例如size(),length(),当表达式引用这些属性时,OGNL会调用相应的方法,这就是伪属性,比如获取一个List的大小,可以使用<s:property value="testList.size()"/>。
List的伪属性:size,isEmpty,iterator。
Set的伪属性:size,isEmpty,iterator。
Map的伪属性:size,isEmpty,keys,values。
Iterator(迭代)的伪属性:next,hasNext。
Enumeration(枚举)的伪属性:next,hasNext,nextElement,hasMoreElements。
8.获取集合中元素的实质就是调用它的toString()方法:
OGNL表达式还可以直接获取集合中的元素,事实上是在调用集合的toString()方法,所以我们可以根据实际情况通过重写集合的toString()方法来实现个性化输出,甚至它还可以像访问数组那样,直接testList[2]获取集合的元素,但这种方法只适用于List,不适用于Map。因为Map的索引是key,不是数值,另外,由于HashSet的元素是没有顺序的,所以也不能用下标获取单个元素。
9.利用投影获取属性:利用投影获取List中对象的userName属性时,其中{}表示的是一个集合,stus.{userName}就表示将stus中所有的userName属性取出组成一个新的列表。
10.利用选择获取属性:
OGNL表达式是很灵活的,可以同时使用选择技术与投影技术获得属性,#this代表当前元素, 问号?是把所有满足条件的元素都取出来,上箭头^是开始的意思,美元符号$是结束的意思,并且这三个符号返回的都是List列表。
11.补充:
(1).当OGNL取不到值的时候,它不会报错,而是什么都不显示
(2).<s:property value="[0]"/>返回的是ValueStack中从上至下的所有的Object,<s:property value="[1]"/>返回的是ValueStack中从上至下的第二个Objec。
(3).<s:property value="[0].username"/>返回的是成员变量username的值,假设ValueStack中存在两个Action的话,如果第一个Action如果没有username变量,那么它会继续找第二个Action。那么在什么情况下ValueStack中会存在两个Action呢?
答案是在struts.xml中配置的是从一个Action通过<result type="chain">跳转到另一个Action。
(4).<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>, 在Struts2.1.6中必须设置struts.ognl.allowStaticMethodAccess为true之后,才允许使用OGNL访问静态方法。而在Struts2.0.11则无需设置,即可直接访问。
12.接下来附上一个例子,通过这个例子可以了解如何通过OGNL表达式获取Action中的属性值或者Action对象中的某个属性值,也可以知道如何调用Action中对象里面的普通方法和Action类的普通方法:
(1).新建一个struts2项目,项目名为Struts2_OGNL,首先新建一个Action类,此类继承于ActionSupport类,代码如下:
package com.gk.action; import com.gk.bean.Cat; import com.gk.bean.User; import com.opensymphony.xwork2.ActionSupport; public class OgnlAction extends ActionSupport { private String username; private String password; private User user=new User(); private Cat cat=new Cat(); public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public String execute(){ return SUCCESS; } }
(2).其中又有几个JavaBean类,分别为User,Cat,Dog类,代码分别如下:
User.java文件代码:
package com.gk.bean; public class User { private int age = 8; public User() { } public User(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "user"+age; } }
Cat.java文件代码:
package com.gk.bean; public class Cat { private Dog friend; public Dog getFriend() { return friend; } public void setFriend(Dog friend) { this.friend = friend; } public String miaomiao(){ return "miaomiao"; } }
Dog.java文件代码:
package com.gk.bean; public class Dog { private String name; public Dog(){ } public Dog(String name){ this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString(){ return "dog"+name; } }
(3).其中又有两个JSP页面,一个是index.jsp页面,一个为ognl.jsp页面,代码分别如下:
index.jsp文件代码:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <a href="ognl?username=u&password=p">ognl</a> </body> </html>
ognl.jsp代码如下,通过struts2的标签和OGNL表达式来获取Action的属性:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'ognl.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <ol> <li>访问值栈中的:username = <s:property value="username " /></li> <li>访问值栈中的:password=<s:property value="password" /></li> <li>访问值栈中对象的普通属性(get set方法):<s:property value="user.age" /></li> <li>访问值栈中对象的普通属性(get set方法):<s:property value="cat.friend.name" /></li> <li>访问值栈中对象的普通方法<s:property value="cat.miaomiao()"/></li> </ol> <s:debug></s:debug> </body> </html>
(4).struts.xml配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant> <constant name="struts.devMode" value="true"></constant> <package name="ognl" namespace="/" extends="struts-default"> <action name="ognl" class="com.gk.action.OgnlAction"> <result>/ognl.jsp</result> </action> </package> </struts>
13.此时我们访问index.jsp页面,结果如下图所示:
点击此链接:
查看一下值栈的内容和上下文环境:
(1).我们可以看到通过超链接传入参数username和password,然后取出值栈里的username和password的属性,然后我们又看到值栈内容中有user属性,我们通过<s:property value="user.age" />获取到了8,原因是我们在User类里面把age初始化为8,在Action类中使用User user=new User这行代码实例化了user对象,然后通过toString方法输出来,所以值栈中user属性的值为user8。
(2).我们通过<s:property value="cat.friend.name" />想要获取cat对象中的friend属性的名字,但是取不出来,是因为我们没有把cat.friend.name这个参数传进去,如果我们输入地址:http://localhost:8083/Struts2_OGNL/ognl?username=u&password=p&cat.friend.name=Lc 的话,就可以取出来了,这也是OGNL如何访问值栈中的内容。
(3)如果我们输入http://localhost:8083/Struts2_OGNL/ognl?username=u&password=p&user.age=90 这个地址的话,年龄就会变为90岁了:
(4).如果我们把User.java文件中public User(){}这个无参构造方法删除掉,然后在OgnlAction中的代码中不实例化这个User类,即OgnlAction.java文件代码如下:
package com.gk.action; import com.gk.bean.Cat; import com.gk.bean.User; import com.opensymphony.xwork2.ActionSupport; public class OgnlAction extends ActionSupport { private String username; private String password; private User user; private Cat cat=new Cat(); public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public String execute(){ return SUCCESS; } }
Use.java文件代码修改成:
package com.gk.bean; public class User { private int age = 8; public User(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "user"+age; } }
然后在输入http://localhost:8083/Struts2_OGNL/ognl?username=u&password=p 发现age内容为空了。
如果我们输入地址为:http://localhost:8083/Struts2_OGNL/ognl?username=u&password=p&user.age=23 看能否把age的值23传进去,发现报了个错误,这个错误是实例化User出错,所以一般我们的域模型,即JavaBean对象永远都要提供一个无参的构造方法,要不然就不知道实例化哪个类了。
(5).我们通过<s:property value="cat.miaomiao()"/>这个标签可以直接获取cat对象的miaomiao()方法。如果想取得OgnlAction类的普通方法的话,即Action类普通方法,假如Action类中有个返回类型为String的普通方法mm()的话,可以直接使用<s:property value="mm()"/>,这样就可以获得该Action类普通方法的返回值。
(6).其中为什么OGNL叫对象图导航语言呢?从上面这个例子来说,访问OgnlAction类的user对象的age属性,就可以使用user.age来取到,如果访问OgnlAction类中的cat对象的miaomiao方法,可以使用cat.miaomiao(),如果想访问cat对象的friend对象中的name属性,就用cat.friend.name,这样就可以取得Dog的friend对象的name属性,但是必须要传入参数值进去。
14.本来还想再附上多两个例子的,其中一个例子是使用OGNL来访问静态方法和静态属性的,但是昨天晚上写代码时发现了一些警告和错误,想专门写一篇博客来记录,所以就先写到这,基本的做法和说明都在上面已经讲述。后面我写好访问静态方法和静态属性的那篇文章后,在附上地址。
15.以上内容仅供大家学习和参考,谢谢!