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 表達式,否則,標簽屬性的值會被當做是一個普通的字符串來處理。
struts-ognl.zip