OGNL
OGNL是Object-Graph Navigation Language对象图导航语言
的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。
##基本介绍
OGNL可以让我们用非常简单的表达式访问对象层,例如,当前环境的根对象为user1,则表达式person.address[0].province可以访问到user1的person属性的第一个address的province属性。
这种功能是模板语言的一个重要补充,象jsp2.0,velocity,jelly等等,都有类似的功能,但是ognl比它们完善得多,而且以一个独立的lib出现,方便我们构建自己的框架。
webwork2和现在的Struts2.x中使用OGNL取代原来的EL来做界面数据绑定,所谓界面数据绑定,也就是把界面元素(例如一个textfield,hidden)和对象层某个类的某个属性绑定在一起,修改和显示自动同步。
和struts1.x的formbean相比,这样做的好处非常明显:在webwork中不需要为每个页面专门写formbean,可以直接利用对象层的对象。例如在对象设计中,我们的User和Person是分开的,而一个注册用户界面需要填写两者的内容,在webwork中,就可以保持后台的对象结构,把属于用户属性的界面元素用user.person.xxx绑定,把属于账号属性的界面元素用user.xxx绑定。
#Structs2
OGNL,可以方便地操作对象属性的开源表达式语言,使页面更简洁;
支持运算符(如±*/),比普通的标志具有更高的自由度和更强的功能;
Struts 2默认的表达式语言是OGNL,原因是它相对其它表达式语言具有下面几大优势:
- 支持对象方法调用,如xxx.doSomeSpecial();
- 支持类静态的方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
- 支持赋值操作和表达式串联,例如
price=100, discount=0.8, calculatePrice(price*discount),这个表达式会返回80;
- 访问OGNL上下文(OGNL context)和ActionContext;
- 操作集合对象。
- 可以直接new一个对象
Ognl有一个上下文(Context) 概念,就是一个Map结构,它实现了 java.utils.Map 接口,在Structs2中上下文(Context)的实现为ActionContext
OGNL表达式示例
参考视频:http://www.chuanke.com/v7232853-198071-1144871.html
示例一
Person.java
//package ognltest;
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
OGNL1.java
//package ognltest;
import ognl.Ognl;
import ognl.OgnlException;
public class OGNL1 {
public static void main(String[] args) {
Person person = new Person();
person.setName("李未央");
try {
//从person对象中获取name属性值,类似反射,根据属性名获取属性值
Object value = Ognl.getValue("name", person);
System.out.println(value);
} catch (OgnlException e) {
e.printStackTrace();
}
}
}
结果:
李未央
示例2
OGNL2.java
//package ognltest;
import java.util.HashMap;
import java.util.Map;
import ognl.Ognl;
import ognl.OgnlException;
/**
* 对于使用上下文的OGNL
* 若不指定从哪一个对象中查找“name”属性,
* 则OGNL直接从根对象(root)查找,
* 若指定查找对象(使用#号指定,如#person1),
* 则从指定的对象中查找,
* 若指定对象不再上下文中则会抛出异常
* 指定查找对象则必须要保证指定对象在上下文环境中。
* 把集合中某个对象的操作进行了简化
*/
public class OGNL2 {
public static void main(String[] args) {
// 创建一个上下文Context对象,来用保存多个对象
Map<String, Object> context = new HashMap<String, Object>();
Person person1 = new Person();
person1.setName("老舍");
Person person2 = new Person();
person2.setName("朱自清");
Person person3 = new Person();
person3.setName("海子");
context.put("person1", person1);
context.put("person2", person2);
context.put("person3", person3);
Person person4 = new Person();
person4.setName("金庸");
try {
// 根据属性名获取属性值
Object value1 = Ognl.getValue("name", context, person1);
System.out.println("ognl expression\"name\":" + value1);
Object value2 = Ognl.getValue("#person2.name", context, person2);
System.out.println("ognl expression\"#person2.name\":" + value2);
//取得不是根对象person2的属性值,而是person1的name属性值
Object value3 = Ognl.getValue("#person1.name", context, person2);
System.out.println("ognl expression\"#person1.name\":" + value3);]
//以person4为根对象
Object value4 = Ognl.getValue("name", context, person4);
System.out.println("ognl expression\"name\":" + value4);
// #person4.name必须要在容器中
// Object value5 = Ognl.getValue("#person4.name",context, person4);
// System.out.println("ognl expression\"#person4.name\":"+value5);
} catch (OgnlException e) {
e.printStackTrace();
}
}
}
执行结果:
ognl expression"name":老舍
ognl expression"#person2.name":朱自清
ognl expression"#person1.name":老舍
ognl expression"name":金庸
示例3
OGNL3.java
//package ognltest;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
/**
* 使用OGNL调用方法也非常简单,
* 对应成员方法调用,只需要给出方法的名称+(),
* 若有参数,直接写在括号内,与一般调用Java方法一致
* 对于静态方法的调用,需要使用如下的格式:
* @ClassName@method,
* 对应静态变量需要使用如下格式:
* @ClassName@field
*/
public class OGNL3 {
public static void main(String[] args) {
// OGNL提供了一个上下文类,他实现了Map接口
OgnlContext context = new OgnlContext();
Person person1 = new Person();
person1.setName("项羽诸葛");
Person person2 = new Person();
person2.setName("李青盲僧");
Person person3 = new Person();
person3.setName("张飞");
context.put("person1", person1);
context.put("person2", person2);
context.put("person3", person3);
context.setRoot(person1);
//context.setRoot(person2);
//context.setRoot(person3);
try {
// 调用成员方法
Object value = Ognl.getValue("name.length()", context, context.getRoot());
System.out.println("person1 name length is:" + value);
Object upperCase = Ognl.getValue("#person2.name.toUpperCase()", context, context.getRoot());
System.out.println("person2 name upperCase is;" + upperCase);
Object invokeWithArgs = Ognl.getValue("name.charAt(2)", context, context.getRoot());
System.out.println("people1 name.charAt(2) is:" + invokeWithArgs);
// 调用静态方法
Object min = Ognl.getValue("@java.lang.Math@min(4,10)", context, context.getRoot());
System.out.println("min(4,10)is :" + min);
// 调用静态变量
Object e = Ognl.getValue("@java.lang.Math@E", context, context.getRoot());
System.out.println("E is :" + e);
} catch (OgnlException e) {
e.printStackTrace();
}
}
}
执行结果:
person1 name length is:4
person2 name upperCase is;李青盲僧
people1 name.charAt(2) is:诸
min(4,10)is :4
E is :2.718281828459045
若context.setRoot(person2);
执行结果为:
person1 name length is:4
person2 name upperCase is;李青盲僧
people1 name.charAt(2) is:盲
min(4,10)is :4
E is :2.718281828459045
示例4
Classroom.java
包含Classroom类和Student类
//package ognltest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Classroom {
private List<String> students = new ArrayList<String>();
public List<String> getStudents() {
return students;
}
public void setStudents(List<String> students) {
this.students = students;
}
}
class Student{
private Map<String, Object> contactWays = new HashMap<>();
public Map<String, Object> getContactWays() {
return contactWays;
}
public void setContactWays(Map<String, Object> contactWays) {
this.contactWays = contactWays;
}
}
OGNL4.java
//package ognltest;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
/**
* OGNL不仅可以操作集合对象,还可以创建集合对象,
* 对集合操作与属性的操作没什么不同,
* 需要注意的是OGNL人为List和Array是一样的
* 使用OGNL创建List集合使用{},创建Map对象是使用#{}
*/
public class OGNL4 {
public static void main(String[] args) {
// OGNL提供了一个上下文类,他实现了Map接口
OgnlContext context = new OgnlContext();
Classroom classroom = new Classroom();
classroom.getStudents().add("摇滚");
classroom.getStudents().add("轻音乐");
classroom.getStudents().add("民谣");
classroom.getStudents().add("流行");
classroom.getStudents().add("电子");
Student student = new Student();
student.getContactWays().put("loveNumber", "520");
student.getContactWays().put("foreverNumber", "1314");
student.getContactWays().put("happyNumber", "1111");
context.put("classroom", classroom);
context.put("student", student);
context.setRoot(classroom);
try {
// 获得classroom的students集合
Object collection = Ognl.getValue("students", context, context.getRoot());
System.out.println("students collection is:" + collection);
// 获得classroom的students集合
Object firststudent = Ognl.getValue("students[0]", context, context.getRoot());
System.out.println("first student is:" + firststudent);
// 调用集合的方法
Object size = Ognl.getValue("students.size()", context, context.getRoot());
System.out.println("students collection size is:" + size);
System.out.println("------split line---------");
Object mapCollection = Ognl.getValue("#student.contactWays", context, context.getRoot());
System.out.println("mapColection is :" + mapCollection);
Object firstElement = Ognl.getValue("#student.contactWays['loveNumber']", context, context.getRoot());
System.out.println("the first element of contactWays is :" + firstElement);
//相当于System.out.println(student.getContactWays().get("loveNumber"));
System.out.println("------split line---------");
// 创建集合
Object createCollection = Ognl.getValue("{'aa','bb','cc','dd'}", context, context.getRoot());
System.out.println(createCollection);
// 创建Map集合
Object createMapCollection = Ognl.getValue("#{'天气':'晴','黄昏':'日落'}", context, context.getRoot());
System.out.println(createMapCollection);
} catch (OgnlException e) {
e.printStackTrace();
}
}
}
执行结果:
students collection is:[摇滚, 轻音乐, 民谣, 流行, 电子]
first student is:摇滚
students collection size is:5
------split line---------
mapColection is :{foreverNumber=1314, happyNumber=1111, loveNumber=520}
the first element of contactWays is :520
------split line---------
[aa, bb, cc, dd]
{天气=晴, 黄昏=日落}
示例5
Human.java
//package ognltest;
import java.util.ArrayList;
import java.util.List;
public class Human {
private String name;
private String sex;
private int age;
private List<Human> friends = new ArrayList<Human>();
public Human() {
}
public Human(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Human> getFriends() {
return friends;
}
public void setFriends(List<Human> friends) {
this.friends = friends;
}
@Override
public String toString() {
return "Human [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}
OGNL5.java
//package ognltest;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class OGNL5 {
public static void main(String[] args) {
OgnlContext context = new OgnlContext();
Human human = new Human();
human.setName("冬音");
human.setSex("male");
human.setAge(22);
human.getFriends().add(new Human("钢琴", "female", 21));
human.getFriends().add(new Human("长笛", "female", 12));
human.getFriends().add(new Human("小提琴", "male", 23));
human.getFriends().add(new Human("风琴随风如琴似梦", "male", 23));
human.getFriends().add(new Human("萨克斯", "female", 43));
human.getFriends().add(new Human("单簧管", "female", 45));
context.put("human", human);
context.setRoot(human);
try {
/**
* OGNL过滤集合的语法:collection.{?expression}
* ?是获得所有符合逻辑的元素
* ^获得符合逻辑的第一个元素
* $获得符合逻辑的最后一个元素
*/
Object filterCollection = Ognl.getValue("friends.{?#this.name.length()>7}", context, context.getRoot());
System.out.println("filterCollection is:" + filterCollection);
System.out.println("-----------呆萌的分隔线------------");
// OGNL投影集合的语法为:collection.{expression}
Object projectionCollection = Ognl.getValue("friends.{name}", context, context.getRoot());
System.out.println("projectionCollection is:" + projectionCollection);
} catch (OgnlException e) {
e.printStackTrace();
}
}
}
执行结果:
filterCollection is:[Human [name=风琴随风如琴似梦, sex=male, age=23]]
-----------呆萌的分隔线------------
projectionCollection is:[钢琴, 长笛, 小提琴, 风琴随风如琴似梦, 萨克斯, 单簧管]
若OGNL格式输入错误,会提示下面的格式错误
Was expecting one of:
"," ...
"=" ...
"?" ...
"||" ...
"or" ...
"&&" ...
"and" ...
"|" ...
"bor" ...
"^" ...
"xor" ...
"&" ...
"band" ...
"==" ...
"eq" ...
"!=" ...
"neq" ...
"<" ...
"lt" ...
">" ...
"gt" ...
"<=" ...
"lte" ...
">=" ...
"gte" ...
"in" ...
"not" ...
"<<" ...
"shl" ...
">>" ...
"shr" ...
">>>" ...
"ushr" ...
"+" ...
"-" ...
"*" ...
"/" ...
"%" ...
"instanceof" ...
"." ...
"(" ...
"[" ...
"}" ...
<DYNAMIC_SUBSCRIPT> ...
]
OGNL在Structs2中的运用
参考:http://blog.csdn.net/tjcyjd/article/details/6850203
Structs2 中的OGNL Context实现者为ActionContext。
OGNL Context 包含的对象
ValueStack(值栈,根对象)
parameters
request
session
application
attr
当Structs2 接收一个请求时,会迅速创建ActionContext,ValueStack,action。然后把 action 存放进ValueStack,所以action的实例变量可以被OGNL访问。
值栈(ValueStack)
值栈(ValueStack)是Structs 2 中的根对象。
可以在值栈中放入、删除、查询对象。访问值栈中的对象不用“#”。
Struts2总是把当前Action实例放置在栈顶。所以在OGNL中引用Action中的属性也可以省略“#”。
ValueStack的实现类为 OgnlValueStack,该对象存放的一组对象。在 OgnlValueStack 类里有一个 List 类型的 root 变量,就是使用它存放一组对象
在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里的对象的属性,搜索顺序从栈顶对象开始寻找,一直向下寻找
Struts2的标签库都是使用OGNL表达式来访问ActionContext中的对象数据的。如:
<s:propertyvalue="xxx"/>。
${foo} //在jsp页面中,获得值栈中某个对象的foo属性
#符号
访问非根对象属性,需要加#前缀。
- application 对象 用于访问ServletContext
#application.userName 或者 #application['userName']
相当于
//调用ServletContext的getAttribute("userName")
- session 对象 用来访问HttpSession
#session.userName 或者 session['userName']
相当于
session.getAttribute("userName")
- request 对象 用来访问 HttpServletRequest属性的Map
#request.userName 或者 #request['userName']
相当于
request.getAttribute("userName")
- parameter对象 用来访问HTTP的请求参数
#parameters.userid 或者 #parameters['userid']
相当于
request.getparameter("userid")
- attr 对象 用于按 page–>request–>session–>application 顺序访问其属性
##OGNL取值示例
index.jsp通过action跳转到ognl.jsp,携带请求参数,action对session,application,request赋值,传递设置好的参数,ognl.jsp显示
structs.xml
<action name="ognl" class="action.OgnlAction">
<result>/ognl.jsp</result>
</action>
OgnlAction.java
//package action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class OgnlAction extends ActionSupport {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String execute() throws Exception {
message="hello";
ActionContext context = ActionContext.getContext();
context.put("request1", "request");
context.getSession().put("session1", "session");
context.getApplication().put("application1", "appication");
return SUCCESS;
}
}
index.jsp
...
<body>
<a href="ognl.action?user=">访问ognl.jsp</a>
</body>
..
login.jsp
..
<body>
0.EL表达式获取:${message}<br/>
1.获取message变量:<s:property value="message"/><br/>
2.获取request变量:<s:property value="#request['request1']"/><br/>
3.获取session变量:<s:property value="#session['session1']"/><br/>
4.获取application变量:<s:property value="#application.application1"/><br/>
5.获取请求参数:<s:property value="#parameters.user"/><br/>
</body>
..
执行结果:
0.EL表达式获取:hello
1.获取message变量:hello
2.获取request变量:request
3.获取session变量:session
4.获取application变量:appication
5.获取请求参数:狄川是
在jsp页面中通过EL表达式
${request1},${session1},${application1},${param.user}
,同样可以获取上面的值
采用OGNL表达式创建List/Map集合对象
list.jsp
<%@ taglib uri="/struts-tags" prefix="s" %>
..
<body>
<!-- 创建list集合: -->
<s:set name="list1" value="{'A梦','B梦','C梦'}"></s:set>
<s:iterator value="#list1" id="n">
<s:property value="n"/>,
</s:iterator>
<br/>
<!-- 创建map集合 -->
<s:set name="map" value="#{'天':'地','玄':'黄' }"></s:set>
<s:iterator value="#map" >
<s:property value="key"/>=<s:property value="value"/><br/>
</s:iterator>
</body>
结果:
A梦, B梦, C梦,
天=地
玄=黄
set标签将某个值放入到指定范围
scope:指定变量放置的范围
action 默认放置在OGNL Context中
value 赋给变量的值,未设置,则将值栈栈顶的值赋给变量
###OGNL表达式判断对象是否存在集合中
判断创建的map集合是否存在
<hr color="red">
<s:if test="'map' in {'map',map1}">
map存在
</s:if>
<s:else>
map不存在
</s:else>
<br/>
<s:if test="'map' not in{'map','map2'">
map集合不在{'map','map2'}
</s:if>
<s:else>
map集合在{'map','map2'}
</s:else>
执行结果:
map存在
map集合在{'map','map2'}
OGNL表达式的投影功能
BookAction.java
//package action;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class BookAction extends ActionSupport {
private List<Book> books;
@Override
public String execute() throws Exception {
books = new ArrayList<>();
books.add(new Book("大唐英雄传",23.6));
books.add(new Book("碧血剑",32.5));
books.add(new Book("法医秦明",43.4));
return SUCCESS;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
<action name="book" class="action.BookAction">
<result>/book.jsp</result>
</action>
Book.java
//package action;
public class Book {
private String title;
private double price;
public Book() {
super();
}
public Book(String title, double price) {
super();
this.title = title;
this.price = price;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setPrice(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
}
book.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>这里是book.jsp</h2>
<!-- ?是获得所有符合逻辑的元素
^获得符合逻辑的第一个元素
$获得符合逻辑的最后一个元素 -->
<s:iterator value="books.{?#this.price>30}">
<s:property value="title"/>$<s:property value="price"/><br/>
</s:iterator>
</body>
</html>
执行结果:
price 大于30的输入
这里是book.jsp
碧血剑$32.5
法医秦明$43.4
<s:iterator>
标签
<s:iterator value="" id="" status="">
<s:property value=""/>
</s:iterator>
1.value属性:可选的属性,value属性是指一个被迭代的集合,使用ognl表达式指定,如果为空的话默认就是ValueStack栈顶的集合
2.id属性:可选属性, 是指集合元素的id
3.status属性:可选属性,该属性在迭代时会产生一个IteratorStatus对象,该对象可以判断当前元素的位置,包含了以下属性方法
int getCount(); 迭代元素个数
int getIndex(); 迭代元素当前索引
boolean isFirst(); 是否为第一个
boolean isEven(); 是否为偶
boolean isLast(); 是否最后一个
bolean isOdd(); 是否为奇