OGNL(Object Graph Navigation Language),是一种表达式语言EL(Exression Language)。使用这种表达式语言,你可以通过某种表达式语法,存取Java对象树中的任意属性、调用Java对象树的方法、同时能够自动实现必要的类型转化。在Struts2里采用了XWork的OGNL方案,构建了OGNLValueStack的机制,从而解决了从View层传递到Controller层的数据流传的不匹配性。
OGNL的表达式的使用
OGNL基本的表达式是将对象的引用值用点串联起来,从左到右每一次表达式的值都作为结果成为当前对象,后面的表达式在这个对象的基础上再进行计算,直到最后的表达式完成为止,返回最终计算的结果。一些常用的表达式有:
1. 基本对象树的访问
对象树的访问就是通过使用点号将对象的引用串联起来进行。
例如:name,department.name,user.department.factory.manager.name
2. 对容器变量的访问
对容器变量的访问,通过#符号加上表达式进行。
例如:#name,#department.name,#user.department.factory.manager.name
3. 使用操作符号
OGNL表达式中能使用的操作符基本跟Java里的操作符一样,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,还能使用 mod, in, not in等。
4. 容器、数组、对象
OGNL支持对数组和ArrayList等容器的顺序访问:
例如:group.users[0]
同时,OGNL支持对Map的按键值查找:
例如:#session['mySessionPropKey']
不仅如此,OGNL还支持容器的构造的表达式:
例如:{"green", "red", "blue"}构造一个List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}构造一个Map
你也可以通过任意类对象的构造函数进行对象新建:
例如:new java.net.URL("http://localhost/")
5. 对静态方法或变量的访问
要引用类的静态方法和字段,他们的表达方式是一样的@class@member或者@class@method(args):
例如:@com.javaeye.core.Resource@ENABLE,@com.javaeye.core.Resource@getAllResources
6. 方法调用
直接通过类似Java的方法调用方式进行,你甚至可以传递参数:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
7. 投影和选择
OGNL支持类似数据库中的投影(projection) 和选择(selection)。
投影就是选出集合中每个元素的相同属性组成新的集合,类似于关系数据库的字段操作。投影操作语法为 collection.{XXX},其中XXX 是这个集合中每个元素的公共属性。
例如:group.userList.{username}将获得某个group中的所有user的name的列表。
选择就是过滤满足selection 条件的集合元素,类似于关系数据库的纪录操作。选择操作的语法为:collection.{X YYY},其中X 是一个选择操作符,后面则是选择用的逻辑表达式。而选择操作符有三种:
? 选择满足条件的所有元素
^ 选择满足条件的第一个元素
$ 选择满足条件的最后一个元素
例如:group.userList.{? #this.name != null}将获得某个group中user的name不为空的user的列表。
OGNL的上下文环境
其实ActionContext就是Struts2中OGNL的上下文环境。它维护着一个Map的结构,通常OGNL的上下文环境如下所示:
|--request
|--application
context map--- |--OgnlValueStack(root) [ user, action, ... ]
|--session
|--parameters
在Struts2中Action是作为OGNL操作的根对象,但是如何来操作这个根对象呢,所以OGNL又引入了ValueStack的概念,ValueStack是一个堆栈结构,将Action里的各个对象有序存入进去,对于某个OGNL表达式来说,OGNL将自堆栈顶部开始查找,并返回第一个符合条件的对象元素。所以ValueStack也是这个上下文环境中的根对象,而除了这个根对象以外,Struts2还在这个上下文环境中放了许多额外的变量,而这些变量多数都是被XWork封装过的Servlet对象,例如request,session,servletContext(application)等,这些对象都被封装成Map对象,随着ActionContext作用于整个Action执行的生命周期中。我们可以使用"#requet"访问HttpServletRequest对象, "#session"访问HttpSession对象。
根对象和上下文环境访问的区别
A) 针对根对象(Root Object)的操作,表达式是自根对象到被访问对象的某个链式操作的字符串表示。
B)针对上下文环境(Context)的操作,表达式是自上下文环境(Context)到被访问对象的某个链式操作的字符串表示,但是必须在这个字符串的前面加上#符号,以表示与访问根对象的区别。
如以下为"#user"和"#request"的用法,"#"表示访问环境/上下文中的对象。
?#user.name // 取用户的姓名
?#user.age // 取用户年龄
?#user.birthday // 取用户生日
?#user.customer.name // 取用户所属客户的名称
?#request.parameters // 取请求参数 如以下访问根对象user的用法。
?name // 取用户的姓名
?age // 取用户年龄
?birthday // 取用户生日
?customer.name // 取用户所属客户的名称