一、OGNL是什么
OGNL(Object Graph Nvigation Language,对象图导航语言)是一个开源的表达式引擎。通过使用OGNL的表达式语法可以存取Java对象树的任意属性和调用Java对象树的方法等。
我们通过一个示例来了解OGNL。
分别创建三个实体类(全部省略了getter、setter方法):
//学生类
public class Student {
private String name;
private Integer age;
private Grade grade;
}
//班级类
public class Grade {
private String name;
private Teacher teacher;
}
//老师类
public class Teacher {
private String name;
}
根据上面三个实体对象的引用,可以形成一个这样的依赖图:
student
|--name
|--grade
|--name
|--teacher
|--name
通过这样的依赖关系图,可以根据student对象导航到grade对象,继而通过grade对象导航到teacher对象。通过Java代码获取如下:
String teacherName = student.getGrade().getTeacher().getName();
对象图就是指对象的依赖关系图,导航就是指根据依赖关系的一个对象定位过程。
那么,我们来看OGNL是如何使用表达式语言来完成导航操作的:
//取值表达式,都是基于对象的getter方法进行导航的
String teachName = (String)Ognl.getValue("grade.teacher.name", student);
二、OGNL的基本操作
1、OGNL的三个要素
Ognl的getValue有两个参数:
第一个参数是表达式。
表达式是整个OGNL的核心,所有OGNL操作都是针对表达式解析后进行的。它表明了此次OGNL操作要“做什么”。表达式就是一个带有语法含义的字符串,这个字符串规定了操作的类型和操作的内容。
OGNL支持大量的表达式语法,不仅支持这种“链式”猫鼠对象访问路径,还支持在表达式中进行简单的计算。
第二个参数是Root对象。
Root对象也就是操作对象。表达式规定了“做什么”,而Root对象则规定了“对谁操作”。
而实际上OGNL的取值还需要一个上下文环境。
设置了Root对象,OGNL可以对Root对象进行取值或写值等操作,那么Root对象放在哪里,OGNL又在哪里对它进行的操作呢?这个操作对象所在环境就是OGNL的上下文环境(Context)。上下文环境规定了OGNL的操作“在哪里进行”。
OGNL的上下文环境就是OgnlContext,它是一个Map结构的类,并且OGNL支持使用Map来构建一个OGNL的上下文环境。这样我们除了Root对象,还可以从上下文环境中获取各类对象。
例如,下面的代码示例:
Student s1 = new Student("student1");
Student s2 = new Student("student2");
Student s3 = new Student("student3");
Map context = new HashMap();
context.put("s1", s1);
context.put("s2", s2);
String names = (String)Ognl.getValue("#s1.name+‘,‘+#s2.name+‘,‘+name", context, s3 );
输出:
student1,student2,student3
2、对Root对象和上下文环境的访问
对于Root对象的访问,表达式可以直接写Root对象属性。而要访问上下文环境中的对象,表达式中要通过#来访问。
比如获取上下文环境中的s1和s2对象,就需要在前面加上#来访问。而访问Root对象(s3),可以直接使用name。
在前面第一个例子中,只设置了Root对象参数,OGNL会构建一个默认的上下文环境,并将Root对象以key=root的形式存储在上下文环境中。
所以,我们也可以这样获取:
String teachName = (String)Ognl.getValue("#root.grade.teacher.name", student);
3、方法调用
OGNL的方法调用表达式和Java的方法调用非常相似,通过"对象.方法()"就可以完成调用,并且可以传递参数。
示例:
String gradeName = (String)Ognl.getValue("grade.getName()", student);
4、对静态变量的访问
使用OGNL访问静态变量的表达式语法是:@[class]@[field/method]。注意class需要是完整类名,也就是说需要包含包名。
示例:
//访问com.boya.struts2.util.Constant类中名为SCHOOLNAME的属性值
@[email protected]
//调用com.boya.struts2.util.Constant类中名为get的方法
@[email protected](‘key‘)
5、使用操作符进行简单计算
OGNL支持的操作符基本和Java里的操作符一样,除了+、-、*、/、++、--、==之外,还支持mod、in、not in等。
2+5 //加
‘hello‘+‘world‘ //字符串连接
5 mod 2 //取模
i++ //递增
s in list //判断对象是否在集合中
6、访问数组和容器
OGNL支持以数组下标的方式访问数组,也可以用于List集合。对于Map集合,同样支持根据key值进行访问。
//访问grade中的学生集合的第一个对象的name
grade.students[0].name
//根据key值访问map
map[‘key‘]
7、集合操作的投影和选择
OGNL支持类似数据库操作的投影和选择功能。
就像Hibernate的投影查询一样,可以使用这样的语法:collection.{attrName}来返回指定属性值的集合
选择就是对集合的过滤,语法为:collection.{X expression},其中X是一个选择操作符,expression则为过滤用的逻辑表达式。选择操作符有三种:
? 返回满足条件的所有元素
^ 返回满足条件的第一个元素
$ 返回满足条件的最后一个元素
示例:
//返回grade对象中students集合里面那么属性构成的集合
grade.students.{name}
//返回grade对象中students集合里面name不为null的元素构成的集合
grade.students.{? #this.name!=null}
8、构造对象
OGNL支持通过表达式来构造对象。构造方式主要有三种:
a、构造List:使用{},元素之间使用逗号(,)进行分隔
b、构造Map:使用#{},key-value直接使用冒号分隔,每组键值对使用逗号分隔
c、构造对象:直接使用对象的构造函数构造
示例:
//构造List
{‘a‘,‘b‘,‘c‘}
//构造Map
#{‘key1‘:‘value1‘,‘key2‘:‘value2‘}
//构造对象(必须使用完整类名)
new com.boya.struts2.entity.Student(‘boya‘)
三、#、%、$符号在OGNL的用途总结
1、#符号的三种用途
a、加在普通OGNL表达式前面,用于访问OGNL上下文环境中的变量
b、使用#{}语法,用于构造Map
c、#this表示对this指针的引用
2、%符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值。
示例:
<s:url value="#foobar[‘foo1‘]" />
<s:url value="%{#foobar[‘foo1‘]}" />
3、$符号有两个主要的用途:
a、用于在国际化资源文件中,引用OGNL表达式
b、在Struts 2配置文件中,引用OGNL表达式,如
<action name="AddPhoto" class="addPhoto">
<interceptor-ref name="fileUploadStack" />
<result type="redirect">ListPhotos.action?albumId=${albumId}</result>
</action>
四、ValueStack介绍
Struts1的Action是依赖容器的,Action中就封装了web容器对象,比如request、response等。而Struts2的Action已经彻底的脱离容器,但是我们提交一个Action,这个Action还是需要和web容器进行交互的,Struts2是构造了一个Action的上下文环境,称之为ActionContext,它封装了当前的Action以及与web容器交互的requst、response等对象。并且使用ThreadLoacl模式对每一次请求对会实例话一个新的ActionContext与当前线程绑定,也正因此Struts2的action是线程安全的(和我们见过的Hibernate对Session的封装一样)。
可以说ActionContext封装一个处理web请求的环境,而在这个环境中对请求数据存储传输则是交给了ValueStatic。
ValueStack,被称为值栈,是对OGNL的扩展,Struts2正式通过ValueStack来使用OGNL进行赋值和取值操作的。
ValueStack对OGNL做了怎样的扩展呢?它不仅封装了OGNL的所有功能,并且主要对OGNL的Root对象进行了扩展。ValueStack封装了一个CompoundRoot类型的对象作为root属性,CompoundRoot是一个继承ArrayList的栈存储结构。而所有被压入栈中的对象,都会被视为OGNL的Root对象。在使用OGNL计算表达式时,首先会将栈顶元素作为Root对象,进行表达式匹配,匹配不成功则会依次向下匹配,最后返回第一个成功匹配的表达式计算结果。
因此,Struts2通过ValueStack实现了多Root对象的OGNL操作。
当你提交一个请求,会为这个请求创建一个和web容器交互的ActionContext,与此同时会创建ValueStack,并置于ActionContext之中。而实例化Action之后,就会将这个action对象压入ValueStack中。
在请求“映射“过程中,Struts2则是通过ParametersInterceptor拦截器将提交的参数值封装入对应的Action属性中。
因此action实例可以作为OGNL的Root对象,对于Action中的属性、方法都可以使用OGNL来获取。
我们通过一个示例来演示一下Struts2如何通过ValueStack操作数据。
1、先写一个简单的UserAction,其中只有一个name属性:
public class UserAction extends ActionSupport{
private String name;
public String execute(){
System.out.println(this.name);
return "success";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2、我们直接通过get方式请求,将参数传递给Action:
http://localhost:8080/struts2-ognl/user?name=boya
3、请求传递的参数值,会自动封装入name属性中,在execute方法中可以直接获得这个属性值。
4、请求返回jsp页面,使用Struts2的taglib获取
Name:<s:property value="name"/>
Struts2 Ognl示例:http://download.csdn.net/detail/boyazuo/7706001
Struts2.x教程(二) OGNL和值栈