这两天一直想写OGNL的总结,但发现下不了笔。今天还是咬牙开始写。
OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。 ------百度百科
OGNL我们看到最多的就是和Struts2的标签结合使用,但其实OGNL离开了Struts2也是可以的,只是用在Struts2中,就必须和标签库结合才能使用。
这篇文章就先讲讲OGNL不结合Struts2的一些用法,下篇文章再讲OGNL在Struts2中的用法。
1.使用OGNL前的准备工作
要使用OGNL,得导入相应的jar包。
2.OgnlContext类和Ognl类的介绍
在类中使用OGNL表达式,和两个类息息相关,分别是OgnlContext类和Ognl类。
Ognl类:This class provides static methods for parsing and interpreting OGNL expressions.根据官方解释,这个类是提供一些静态方法去解析表达式。
OgnlContext:This class defines the execution context for an OGNL expression.该类定义OGNL表达式的执行上下文。
public class OgnlContext extends Object implements Map {}
OgnlContext类实现了Map接口,所以它也是一个Map,可以通过put方法往该上下文环境中放元素。该上下文环境中,有两种对象:根对象和普通对象。我们可以使用它的setRoot方法设置根对象。根对象只能有一个,而普通对象则可以有多个。
3.Ognl获取普通对象和根对象的方法
在上下文环境中,有根对象和普通的对象,两者的获取方式有所不同:获取根对象的属性值,可以直接使用属性名作为表达式,也可以使用#对象名.属性名的方式;获取普通对象的属性值,则必须使用#对象名.属性名的方式获取。下面举例说明。
新建一个School类,有学校名称和老师的集合两个属性
package com.codeliu.ognl;
import java.util.List;
public class School {
private String name;
private List teachers;
public School() {}
public School(String name, List teachers) {
super();
this.name = name;
this.teachers = teachers;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getTeachers() {
return teachers;
}
public void setTeachers(List teachers) {
this.teachers = teachers;
}
}
新建一个Teacher类,有姓名、年龄、性别三个属性
package com.codeliu.ognl;
public class Teacher {
private String name;
private String gender;
private int age;
public Teacher(String name, String gender, int age) {
super();
this.name = name;
this.gender = gender;
this.age = age;
}
public Teacher() {}
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;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
新建一个测试类Test
public class Test {
public static void main(String[] args) throws OgnlException {
// 定义一个老师对象
Teacher t1 = new Teacher("CodeTiger", "男", 22);
List lists = new ArrayList();
lists.add(t1);
// 定义一个学校
School s = new School("NJUPT", lists);
// 创建一个OgnlContext实例对象
OgnlContext context = new OgnlContext();
// 把学校和老师放入上下文环境中
context.put("t1", t1);
context.put("s", s);
// 设置学校为根对象
context.setRoot(s);
// 会覆盖前面的根对象
// context.setRoot(t1);
// 解析表达式
// Object expression = Ognl.parseExpression("#s.name");
Object expression = Ognl.parseExpression("name");
// Object expression = Ognl.parseExpression("s.name"); // 出错
// 直接获取根对象的信息,使用#获取上下文中的属性值时,必须使用带Map context参数getValue方法,指定上下文环境
// 如果是获取根对象,没有使用#,则可以使用不带Map context参数getValue方法,我们也可以使用#获取根对象属性值
Object result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 获取普通对象的属性
expression = Ognl.parseExpression("#t1.gender");
// 此时必须指定上下文环境,不然找不到
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通过根对象获取普通对象的属性
// expression = Ognl.parseExpression("teachers[0].name");
expression = Ognl.parseExpression("#s.teachers[0].name");
// expression = Ognl.parseExpression("s.teachers[0].name"); // Exception in thread "main" ognl.NoSuchPropertyException: com.codeliu.ognl.School.s
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
}
}
运行输出
NJUPT
男
CodeTiger
以上有几个地方要注意:
1.根对象只能有一个,当你尝试设置多个的时候,后面的会覆盖前面的。
2.访问根对象中的属性,可以直接使用属性名,或者#对象名.属性名,但不能使用对象名.属性名,不然它会认为这是根对象中的一个属性叫对象名,然后访问这个属性的名叫属性名的属性值。(说的我都懵了)
比如我上面获取学习的名称,使用下面的表达式
Object expression = Ognl.parseExpression("s.name"); // 出错
则会出现下面的错误
Exception in thread "main" ognl.NoSuchPropertyException: com.codeliu.ognl.School.s
提示你找不到School类中的s属性,凭你debug多年的经验,应该一眼就能看出问题所在。
3.不使用#获取根对象中的属性值时, 使用getValue方法可以不指定上下文环境,因为第三个参数已经得到了根对象,它指定去根对象中找。如果获取普通对象的属性,则必须在getValue方法中指定上下文环境,因为你不知道它找不到。
4.通过根对象间接获取普通对象的属性时,比如List集合,使用下标去获取。下面会详细讲。
4.Ognl访问非静态方法、静态方法、静态字段的方法
Ognl不但可以访问我们自己定义的类的属性和方法,还可以访问Java API中的静态方法和静态字段。看下面的代码
// 直接调用根对象中School的getName方 法获取学校名
// expression = Ognl.parseExpression("getName()");
expression = Ognl.parseExpression("#s.getName()");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 调用普通对象中的非静态方法
// expression = Ognl.parseExpression("#t1.getName()");
expression = Ognl.parseExpression("#t1.getName().length()");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 调用根对象中的静态方法
expression = Ognl.parseExpression("getTeacherName()");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 调用API中的静态方法
expression = Ognl.parseExpression("@java.lang.Math@floor(4.5)");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 调用API中的静态属性
expression = Ognl.parseExpression("@java.lang.Math@PI");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
我特地在School类中增加了一个静态方法获取老师集合中第一个老师的姓名,然后使用Ognl调用它。
输出结果如下
NJUPT
9
CodeTiger
4.0
3.141592653589793
5.Ognl获取数组、集合、Map中元素的方法
Ognl表达式可以创建实例对象,以及获取实例对象中的属性,下面我们看看怎么通过Ognl创建一个集合、map以及通过Ognl获取里面的元素。
public class Test2 {
public static void main(String[] args) throws OgnlException {
OgnlContext context = new OgnlContext();
// 通过Ognl可以创建java的实例对象,只有是类的完整路径
Object expression = Ognl.parseExpression("new java.util.ArrayList()");
Object result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通过Ognl可以创建一个初始化的List
expression = Ognl.parseExpression("{'a', 'b', 'c', 'd'}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通过Ognl可以创建一个初始化的Map,注意此时得加上#符号
expression = Ognl.parseExpression("#{'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd'}");
// 创建指定类型的Map
// expression = Ognl.parseExpression("#@java.util.TreeMap@{'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd'}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通过Ognl访问数组中的元素
String[] name1 = {"liu", "xu"};
context.put("name1", name1);
// 直接通过数组名+下标
expression = Ognl.parseExpression("#name1[1]");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通过Ognl访问集合中的元素
List name2 = new ArrayList();
Collections.addAll(name2, name1);
context.put("name2", name2);
// 直接通过集合名+下标
expression = Ognl.parseExpression("#name2[0]");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 通过Ognl访问Map中的元素
Map name3 = new HashMap();
name3.put(1, "liu");
name3.put(2, "xu");
context.put("name3", name3);
// 直接通过map名+key
expression = Ognl.parseExpression("#name3[1]");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
}
}
输出
[]
[a, b, c, d]
{a=aa, b=bb, c=cc, d=dd}
xu
liu
liu
注意点
1.创建集合不用加#,创建map要加#。
2.创建类的一个对象,要使用类的完整路径。
3.要创建带有初始化值的指定类型的List或Map,可以这样#@java.util.TreeMap@{'key':'value','key':'value',......}。
6.Ognl中的投影和过滤
无论投影还是过滤,都是针对于数组、集合和Map而言。
投影:把集合中所有对象的某个属性抽出来,单独构成一个新的集合对象。语法如下
collection.{expression}
过滤:将满足条件的对象,构成一个新的集合返回。语法如下
collection.{?|^|$ expression}
上面?^$的含义如下
?:获得所有符合逻辑的元素。
^:获得符合逻辑的第一个元素。
$:获得符合逻辑的最后一个元素。
了解了上面的,我们就看看下面的代码
public class Test3 {
public static void main(String[] args) throws OgnlException {
Teacher t1 = new Teacher("liu", "男", 22);
Teacher t2 = new Teacher("xu", "女", 22);
Teacher t3 = new Teacher("qian", "男", 30);
Teacher t4 = new Teacher("li", "女", 35);
List lists = new ArrayList();
Collections.addAll(lists, new Teacher[] {t1, t2, t3, t4});
OgnlContext context = new OgnlContext();
context.put("teachers", lists);
// 把所有老师的名字拿出来,投影
Object expression = Ognl.parseExpression("#teachers.{name}");
Object result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 把年龄在20-30之间的所有老师拿出来,过滤
expression = Ognl.parseExpression("#teachers.{? #this.age > 20 && #this.age < 30}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 把年龄在20-30之间的第一个老师拿出来,过滤
expression = Ognl.parseExpression("#teachers.{^ #this.age > 20 && #this.age < 30}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
// 把年龄在20-30之间的最后一个老师拿出来,过滤
expression = Ognl.parseExpression("#teachers.{$ #this.age > 20 && #this.age < 30}");
result = Ognl.getValue(expression, context, context.getRoot());
System.out.println(result);
}
}
输出
[liu, xu, qian, li]
[Teacher [name=liu, gender=男, age=22], Teacher [name=xu, gender=女, age=22]]
[Teacher [name=liu, gender=男, age=22]]
[Teacher [name=xu, gender=女, age=22]]
这个没啥好讲的,记住语法就行了。