OGNL
OGNL ( Object Graph Navigation Language ),对象图导航语言。这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。
在 Struts2 中,OGNL 需要和 Struts2 标签库配套来使用。
OGNL context
|
| -- application
|
| -- session
|
| -- value stack ( root )
context map ---- |
| -- request
|
| -- parameters
|
| -- attr ( searches page, request, session, then application scopes )
|
Struts2 框架将 OGNL context 设置为我们的 ActionContext,并将 ValueStack 作为 OGNL 的根对象。而 Action 则置于 ValueStack 的最顶层。
除此之外,Struts2 框架还把代表 application、request、session 对象的 Map 对象也放到 ActionContext 中,使得 Action 与 Servlet API 解耦。
名称 |
描述 |
ValueStack |
值栈,作为 OGNL 上下文的根对象。通过 KEY 来访问,非根对象需要用 #KEY 来访问 |
parameters |
Map 类型,封装了请求中的所有参数。访问 #parameters.name 相当于调用 HttpServletRequest.getParameter( ) |
request |
Map 类型,封装了 request 对象中的所有属性。访问 #request.name 相当于调用 HttpServletRequest.getAttribute( ) |
session |
Map 类型,封装了 session 对象中的所有属性。访问 #session.name 相当于调用 HttpSession.getAttribute( ) |
application |
Map 类型,封装了 application 对象中的所有属性。访问 #application.name 相当于调用 ServletContext.getAttribute( ) |
attr |
Map 类型,依次从 page、request、session、application 对象中检索属性的值 |
OGNL 访问 Action 中的数据
Action 位于值栈的栈顶位置,而值栈又是 OGNL 的根对象,因此,在 OGNL 表达式中可直接使用属性名称来访问 Action 当中的数据。如:
<s:property value="name" />
实际上,这里是通过调用 Action 当中的 getName( ) 方法来获取得到数据的,而不管 Action 当中是否有一个名称为 name 的属性变量。
因此,如果需要在页面中获取得到 Action 当中的数据,你只需要为你的 Action 类编写 getXX( ) 方法就可以了。
测试环境
package fan.tutorial.model;
import java.util.Set;
public
class Person {
private String sex;
private String name;
private IDCard idcard;
private Set<Address> addressSet;
public
static
final
double VERSION = 1.0;
public Person(){}
public Person(String name, String sex, IDCard idcard, Set<Address> addressSet){
this.sex = sex;
this.name = name;
this.idcard = idcard;
this.addressSet = addressSet;
}
public String getSex() {
return sex;
}
public
void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public
void setName(String name) {
this.name = name;
}
public Set<Address> getAddressSet() {
return addressSet;
}
public
void setAddressSet(Set<Address> addressSet) {
this.addressSet = addressSet;
}
public IDCard getIdcard() {
return idcard;
}
public
void setIdcard(IDCard idcard) {
this.idcard = idcard;
}
public
static
double getVersion() {
return VERSION;
}
}
package fan.tutorial.model;
public
class IDCard {
private
long number;
public IDCard(){}
public IDCard(
long number){
this.number = number;
}
public
long getNumber() {
return number;
}
public
void setNumber(
long number) {
this.number = number;
}
}
package fan.tutorial.model;
public
class Address {
private String name;
public Address(){}
public Address(String name){
this.name = name;
}
public String getName() {
return name;
}
public
void setName(String name) {
this.name = name;
}
}
package fan.tutorial.action;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import fan.tutorial.model.IDCard;
import fan.tutorial.model.Person;
import fan.tutorial.model.Address;
import com.opensymphony.xwork2.Action;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.ApplicationAware;
public
class DataAction
implements Action, RequestAware, SessionAware, ApplicationAware {
private String author;
private String subject;
private Person person;
private List<Person> personList;
private Map<String, String> map;
private Map<String, Object> request;
private Map<String, Object> session;
private Map<String, Object> application;
private
int[] array = {8, 0, 9, 1, 3, 4, 2, 5, 7, 6};
public String execute()
throws Exception {
subject = "fan-tutorial";
Set<Address> addressSet =
new HashSet<Address>(2);
addressSet.add(
new Address("广东茂名"));
addressSet.add(
new Address("广东广州"));
person =
new Person("fan", "male",
new IDCard(3115981L), addressSet);
personList =
new ArrayList<Person>(3);
addressSet =
new HashSet<Address>(1);
addressSet.add(
new Address("云南丽江"));
personList.add(person);
personList.add(
new Person("chen", "female",
new IDCard(3575982L), addressSet));
addressSet =
new HashSet<Address>(1);
addressSet.add(
new Address("广东潮汕"));
personList.add(
new Person("chai", "female",
new IDCard(3115983L), addressSet));
map =
new LinkedHashMap<String, String>(2);
map.put("username", "fan");
map.put("password", "yun");
request.put("message", "hey request");
session.put("message", "hey session");
application.put("message", "hey application");
return SUCCESS;
}
public String getSubject() {
return subject;
}
public Person getPerson() {
return person;
}
public List<Person> getPersonList() {
return personList;
}
public
int[] getArray() {
return array;
}
public Map<String, String> getMap() {
return map;
}
public String getAuthor() {
return author;
}
public
void setAuthor(String author) {
this.author = author;
}
public
void setRequest(Map<String, Object> request) {
this.request = request;
}
public
void setSession(Map<String, Object> session) {
this.session = session;
}
public
void setApplication(Map<String, Object> application) {
this.application = application;
}
}
<
struts
>
<
constant
name
="struts.ognl.allowStaticMethodAccess"
value
="true"
/>
<
package
name
="default"
extends
="struts-default"
>
<
default-action-ref
name
="defaultAction"
/>
<
action
name
="defaultAction"
>
<
result
type
="redirect"
>test?author=fan
</
result
>
</
action
>
<
action
name
="test"
class
="fan.tutorial.action.DataAction"
>
<
result
>/index.jsp
</
result
>
</
action
>
</
package
>
</
struts
>
OGNL 访问对象属性
<
s:property
value
="subject"
/>
<
s:property
value
="person.name"
/>
<
s:property
value
="person.idcard.number"
/>
OGNL 调用方法
<
s:property
value
="person.getName()"
/>
<
s:property
value
="person.name.toUpperCase()"
/>
OGNL 调用静态属性
<
s:property
value
="@fan.tutorial.model.Person@VERSION"
/>
OGNL 调用静态方法
<!--
在 struts.xml 中添加下面这行配置
-->
<!--
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
-->
<
s:property
value
="@fan.tutorial.model.Person@getVersion()"
/>
OGNL 调用构造方法
<
s:property
value
="new fan.tutorial.model.Address('广东茂名').name"
/>
OGNL 使用索引访问数组和列表
<
s:property
value
="array[0]"
/>
<
s:property
value
="personList[0].name"
/>
OGNL 操作符运算
<
s:property
value
="array[0] + 1"
/>
<
s:property
value
="array[0] - 1"
/>
<
s:property
value
="array[0] * 2"
/>
<
s:property
value
="array[0] / 2"
/>
<
s:property
value
="array[0] % 3"
/>
OGNL 逻辑运算符
<
s:set
name
="x"
value
="5"
/>
<
s:property
value
="#x in array"
/>
<
s:property
value
="#x not in array"
/>
<
s:property
value
="#x > array[0]"
/>
<
s:property
value
="#x >= array[0]"
/>
<
s:property
value
="#x < array[0]"
/>
<
s:property
value
="#x <= array[0]"
/>
<
s:property
value
="#x == array[0]"
/>
<
s:property
value
="#x != array[0]"
/>
OGNL 访问命名对象 ( parameters、request、session、application、attr )
<
s:property
value
="#parameters.author"
/>
<
s:property
value
="#request.message"
/>
<
s:property
value
="#session.message"
/>
<
s:property
value
="#application.message"
/>
<
s:property
value
="#attr.message"
/>
OGNL 访问集合的伪属性
类型 |
伪属性 |
伪属性对应的 Java 方法 |
List Set Map |
size isEmpty |
List.size() List.isEmpty() Set.size() Set.isEmpty() Map.size() Map.isEmpty() |
List Set |
iterator |
List.iterator() Set.iterator() |
Map |
keys values |
Map.keySet() Map.values() |
Iterator |
next hasNext |
Iterator.next() Iterator.hasNext() |
<
s:property
value
="personList.size"
/>
<
s:property
value
="personList.isEmpty"
/>
<
s:property
value
="map.keys"
/>
<
s:property
value
="map.values"
/>
<
s:property
value
="personList.iterator.hasNext"
/>
<
s:property
value
="personList.iterator.next.name"
/>
<
s:property
value
="person.addressSet.iterator.hasNext"
/>
<
s:property
value
="person.addressSet.iterator.next.name"
/>
OGNL 迭代集合
类型 |
伪属性 |
伪属性的作用描述 |
IteratorStatus |
index |
当前元素的索引 |
IteratorStatus |
first |
当前元素是否是集合的第一个元素 |
IteratorStatus |
last |
当前元素是否是集合的最后一个元素 |
IteratorStatus |
count |
当前迭代元素的数量,count = index + 1 |
IteratorStatus |
even |
index + 1 是否为偶数 |
IteratorStatus |
odd |
index + 1 是否为奇数 |
<
table
>
<
tr
align
="center"
>
<
td
width
="2%"
>索引
</
td
>
<
td
width
="5%"
>值
</
td
>
<
td
width
="8%"
>当前迭代的数量
</
td
>
<
td
width
="8%"
>迭代奇偶性
</
td
>
<
td
width
="8%"
>集合第一个元素
</
td
>
<
td
width
="8%"
>集合最后一个元素
</
td
>
</
tr
>
<
s:iterator
value
="array"
var
="a"
status
="status"
>
<
tr
align
="center"
>
<
td
>
<
s:property
value
="#status.index"
/>
</
td
>
<
td
>
<
s:property
/>
</
td
>
<
td
>
<
s:property
value
="#status.count"
/>
</
td
>
<
td
>
<
s:if
test
="#status.even"
>偶
</
s:if
>
<
s:if
test
="#status.odd"
>奇
</
s:if
>
</
td
>
<
td
>
<
s:if
test
="#status.first"
>是
</
s:if
>
<
s:else
>否
</
s:else
>
</
td
>
<
td
>
<
s:if
test
="#status.last"
>是
</
s:if
>
<
s:else
>否
</
s:else
>
</
td
>
</
tr
>
</
s:iterator
>
</
table
>
OGNL 投影
如果把集合中的数据想象成是数据库表中的数据,那么,投影就是从这张表中选取某一列所构成的一个新的集合。投影的语法:collection.{expression}
<
s:property
value
="personList.{name}"
/>
OGNL 过滤
OGNL 过滤也称为选择,就是把满足 OGNL 表达式的结果选择出来构成一个新的集合。
过滤的语法:collection.{?expression} 或 collection.{^expression} 或 collection.{$expression}
符号 |
作用 |
? |
选取与逻辑表达式匹配的所有结果 |
^ |
选取与逻辑表达式匹配的第一个结果 |
$ |
选择与逻辑表达式匹配的最后一个结果 |
#this |
代表当前迭代的元素 |
<
s:property
value
="array.{?#this > 5}"
/>
<
s:property
value
="array.{^#this > 5}"
/>
<
s:property
value
="array.{$#this > 5}"
/>
OGNL 投影和过滤
<
s:property
value
="personList.{?#this.sex.equals('female')}.{name}"
/>
<
s:property
value
="personList.{^#this.sex.equals('female')}.{name}"
/>
<
s:property
value
="personList.{$#this.sex.equals('female')}.{name}"
/>
OGNL %{ } 语法
对于 ${ } 也许你并不会陌生,${ } 是 EL 表达式的语法,这里的 %{ } 是 OGNL 表达式的语法。
也许你开始困惑,上面示例不是都在使用 OGNL 表达式吗?!没见 %{ } 出现过啊!好眼力!凡是属于 OGNL 表达式的串,你都可以使用 %{ } 来将它们包裹住,但这不是必须的。例如 <s:property value="expression" /> 中的 expression 在任何时候都是被当做 OGNL 表达式来处理的。
<
s:property
value
="subject"
/>
<!--
subject被OGNL进行表达式求值输出
-->
<
s:property
value
="i love java so much"
/>
<!--
什么都不输出
-->
第2行之所以什么都不输出,是因为执行时环境把 i love java so much 这个字符串也当做是一个 OGNL 表达式来处理了,但在 OGNL 上下文中并找不到与这个 KEY 对应的值,因此什么都没有输出。
这是由于 <s:property /> 标签的 value 属性是 Object 类型引起的,凡是 Object 类型的标签属性的值,都会被当做是一个 OGNL 表达式来处理。
这种情况下的解决办法是:使用单引号将它们引起来,表明这是一个普通的字符串,而不是 OGNL 表达式。
<
s:property
value
="'subject'"
/>
<!--
输出 subject
-->
<
s:property
value
="'i love java so much'"
/>
<!--
输出 i love java so much
-->
再如 <s:textfield value="expression" /> 中的 expression 什么时候被当做 OGNL 表达式来处理就要取决于你是否使用了 %{ } 语法,如果使用了,那么它就是一个 OGNL 表达式,如果没有使用,那么它就只是一个普通的字符串而已。
<
s:textfield
value
="author"
/>
<!--
author被当做普通字符串原样输出
-->
<
s:textfield
value
="%{author}"
/>
<!--
author被OGNL进行表达式求值输出
-->
<
s:textfield
value
="person.name"
/>
<!--
person.name被当做普通字符串原样输出
-->
<
s:textfield
value
="%{person.name}"
/>
<!--
person.name被OGNL进行表达式求值输出
-->
这是由于 <s:textfield /> 标签的 value 属性是 String 类型引起的,凡是非 Object 类型的标签属性的值,是不会被当做一个 OGNL 表达式来处理的,
除非你使用了 %{ expression } 语法,执行时环境才会将 expression 当做是一个 OGNL 表达式来处理。
只有当你理解了上面的2个案例,你才能正确的使用 OGNL 表达式。
实际上规则非常简单,当标签属性的类型为 Object 类型时,标签属性的值就会被当做是一个 OGNL 表达式来处理,因此可省略 %{} ;
当标签属性的类型为 String 类型时,除非你使用了 %{ } 语法告诉执行时环境这是一个 OGNL 表达式,否则,标签属性的值会被当做是一个普通的字符串来处理。