Struts2 OGNL使用详解

jopen 10年前

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 表达式,否则,标签属性的值会被当做是一个普通的字符串来处理。