OGNL表示式和Struts2標籤
OGNL的全稱是Object Graph Navigation Language(物件圖形導航語言),它是一種強大的表示式語言,開發者可以通過簡單一致的表示式語法來讀取和設定java物件的屬性值,呼叫物件的方法,遍歷整個物件的結構。
操作物件!
OGNL有一個上下文(Context)概念,通俗的說就是一個Map結構,它實現了java.util.Map介面,在struts2中上下文(Context)的實現為ActionContext。即Struts2中OGNL Context實現者為ActionContext,它的結構示意圖如下:
當Struts2接受一個請求時,會迅速建立ActionContext,然後建立ValueStack,再建立action。接著把action存放到ValueStack中,所以action中的例項變數可以被OGNL表示式直接訪問。
OGNL表示式訪問各個名稱空間的屬性:
1.訪問上下文(Context)中的物件需要使用#符號標註名稱空間,如#application、#Session...
2.另外OGNL會設定一個根物件(root物件),在struts2中根物件就是ValueStack(值棧)。如果要訪問根物件(即值棧ValueStack)中物件的屬性,則可以省略#名稱空間,直接訪問該物件的屬性即可。
3.在struts2中,根物件ValueStack的實現類為OgnlValueStack,該物件不是存放單個值,而是存放一組物件。在OgnlValueStack類裡面有一個List型別的root變數,就是使用它來存放一組物件
4.在root變數中處於第一位的物件叫做棧頂物件。通常我們在OGNL表示式裡直接寫上熟悉你的名稱即可訪問root變數裡物件的屬性,搜尋順序是從棧頂物件開始尋找,如果棧頂物件不存在該屬性,就會從第二個物件開始尋找,依次往下訪問,知道找到為止。
如:要訪問存在棧頂中的一個name屬性,我們不需要用到#名稱空間的方式,可以直接使用物件屬性名稱的方式訪問,即寫個name即可,但是這樣寫又是無效的,在struts2中,OGNL表示式需要配合struts2標籤才可以使用,應該寫成這樣:<s:property value="name">,這個name就是OGNL表示式。
5.訪問值棧ValueStack中的物件,除了直接寫屬性名稱以外,還可以使用EL表示式直接訪問,如上面的name,可以這樣訪問:${name}。
注:EL表示式只限於訪問值棧ValueStack中的物件的屬性,如果要訪問其他域的屬性還得使用#名稱空間的方式。
Application物件:用於訪問ServletContext,例如:#application.userName或者#application['userName'](注【】的形式在一些特殊字元的時候使用,如#applicaiton['u-name']),這樣相當於呼叫了ServletContext的getAttribute("username");
session物件:用來訪問HttpSession,例如:#sesion.userName或者#session['userName'],相當於呼叫session.getAttribute("userName");
request物件:用來訪問HttpServletRequest屬性,例如:#request.userName或者#request['userName'],相當於呼叫request.getAttribute("userName");
parameters物件:用來訪問Http的請求引數,例如:#parameters.userName或者#parameters['userName'],相當於呼叫request.getParameter("userName");
attr物件:用於按page->request->session->application順序訪問屬性。
在JSP中使用strus2的標籤,必須要引入標籤宣告:
<%@taglib prefix="s" uri="/struts-tags" %>
1.set標籤:用於將某個值放入指定的範圍。
引數:
注:如果沒有設定scope範圍,那麼預設為OGNL Context(訪問時不需要名稱空間,使用#即可)。
測試程式碼:
<body>
<s:set name="age" value="22"></s:set>
</body>
注:上述程式碼沒有指定範圍,即將age放入OGNL Context中,訪問時使用#age即可。
2.property標籤:用於輸出指定的值:
value屬性:可選,指定需要輸出的屬性值,如果沒有該屬性,則預設輸出ValueStack棧頂的值(這個非常重要,有些時候不寫value也可以輸出,是因為要訪問的屬性在棧頂)。
測試程式碼1(訪問request範圍的屬性):
<body>
<%
request.setAttribute("name", "習近平");
%>
<!-- 訪問request範圍內的屬性,要使用#名稱空間的形式 -->
名稱:<s:property value="#request.name"/>
</body>
效果:
測試程式碼2(訪問Session範圍的屬性):
<body>
<%
session.setAttribute("name", "李克強");
%>
<!-- 訪問session範圍內的屬性,要使用#名稱空間的形式 -->
名稱:<s:property value="#session.name"/>
</body>
效果:
測試程式碼3(訪問值棧中的屬性):
/**
* 在請求訪問action的時候,系統會迅速建立ActionContext->valueStack->action
* 並且把action存放在valueStack中,這就是為什麼OGNL表示式可以直接以屬性名稱的方式訪問屬性。
* 因為action存放在了valueStack中。
* @author Liao
*
*/
public class UserAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String execute(){
this.setName("胡錦濤");
return this.SUCCESS;
}
}
在介面訪問:
<body>
<!-- 訪問valueStack中的屬性,不需要使用#名稱空間的形式,直接通過屬性名的方式即可訪問 -->
名稱:<s:property value="name"/> <br>
<!-- 值棧的值也可以直接通過EL表示式來訪問 -->
通過EL表示式獲取的名稱:${name}
</body>
效果:
3.Iterator迭代標籤:用於遍歷集合(java.util.Collection)或者列舉值(java.util.iterator)。
引數:
注:value為可選屬性,指定被迭代的集合,如果沒有設定該屬性,則使用ValueStack棧頂的集合。
在演示之前我們先看另外一個知識點:通過OGNL表示式來建立List/Map集合。
測試程式碼:
<body>
<!-- 通過OGNL可以建立List和Map集合value="{'鄧小平','胡錦濤','習近平'}"表示一個List集合 -->
<s:set name="list" value="{'鄧小平','胡錦濤','習近平'}"/>
<!-- 上述集合沒有指定範圍就表示OGNL Context範圍內的變數,使用#即可 ,iterator標籤中的value表示要迭代的集合-->
<s:iterator value="#list">
<!-- 注:Iterator標籤在迭代集合時,會把當前迭代的物件放在值棧的棧頂,所以使用property標籤輸出時可以不用value -->
<s:property /><br>
</s:iterator>
</body>
注:一定要記住,iterator標籤在迭代集合時會把當前迭代的物件放在值棧的棧頂,這就是為什麼使用property輸出值時可以不寫value屬性的原因。
效果:
使用OGNL建立Map集合:
測試程式碼【通過OGNL可以建立List和Map集合value="#{'key1':'鄧小平','key2':'胡錦濤','key3':'習近平'}"表示一個map集合(注意書寫方式)】:
測試程式碼:
<body>
<s:set name="maps" value="#{'key1':'鄧小平','key2':'胡錦濤','key3':'習近平'}" />
<!-- 上述集合沒有指定範圍就表示OGNL Context範圍內的變數,使用#即可 ,iterator標籤中的value表示要迭代的集合-->
<s:iterator value="#maps">
<!-- 注:Iterator標籤在迭代集合時,會把當前迭代的物件放在值棧的棧頂,所以使用property標籤輸出時可以不用value -->
<s:property value="key" />=<s:property value="value" />
</br>
</s:iterator>
</body>
效果:
OGNL表示式的投影功能,常用操作符:
1.?表示獲得所有符合邏輯的元素。
2.^表示符合邏輯的第一個元素。
3.$表示符合邏輯的最後一個元素。
測試案例:
User物件:
public class User {
private Integer uid;
private String uname;
private Integer age;
public User() {
}
public User(Integer uid, String uname, Integer age) {
this.uid = uid;
this.uname = uname;
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
}
UserAction.java:
/**
* 在請求訪問action的時候,系統會迅速建立ActionContext->valueStack->action
* 並且把action存放在valueStack中,這就是為什麼OGNL表示式可以直接以屬性名稱的方式訪問屬性。
* 因為action存放在了valueStack中。
* 這個類中的users屬性可以直接通過屬性名稱的方式訪問
* @author Liao
*
*/
public class UserAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public String execute(){
users = new ArrayList<User>();
users.add(new User(1, "習近平", 20));
users.add(new User(1, "胡錦濤", 30));
users.add(new User(1, "鄧小平", 40));
return this.SUCCESS;
}
}
前臺投影測試:
<body>
<!-- users是action中的屬性,即存放在ValueStack中,所以訪問users不需要使用#名稱空間的形式 -->
<s:iterator value="users.{?#this.age > 30}">
<s:property value="uname" />:<s:property value="age" />
</br>
</s:iterator>
</body>
注:users是action中的屬性,當然也是ValueStack中的物件,所以可以用屬性名稱的方式直接訪問。在上述程式碼中,直接在集合後緊跟.{}運算子表明用於取出該集合的子集,{}的表示式用於獲取符合條件的元素,this指的是為了從大集合users帥選資料到小集合,需要對大集合users進行迭代,this代表當前迭代的元素。
效果:
iterator標籤中的status屬性用來指定迭代時的IteratorStatus例項,該方法包含如下集合方法:
int getCount(),返回當前迭代了幾個元素。
int getIndex(),返回當前迭代元素的索引。
boolean isEven(),返回當前被迭代元素的索引是否是偶數
boolean isOdd(),返回當前被迭代元素的索引是否是奇數
boolean isFirst(),返回當前被迭代元素是否是第一個元素。
boolean isLast(),返回當前被迭代元素是否是最後一個元素。
有了這些方法,我麼可以做一些特殊效果:
測試程式碼:
<body>
<s:set name="list" value="{'鄧小平','胡錦濤','習近平'}" />
<s:iterator value="#list" status="s">
<!-- 如果是奇數就為紅色,否則為藍色 -->
<font color=<s:if test="#s.odd">red</s:if>
<s:else>blue</s:else>>
<s:property/>
</font>
</s:iterator>
</body>
效果:
引數:
測試代1:
<body>
<s:set name="age" value="22"></s:set>
<!-- 使用ognl接收值 -->
<s:if test="#age > 20">
您的青春已經讓狗給吃了!
</s:if>
<s:elseif test="#age > 35">
七年之癢,勿出軌!
</s:elseif>
<s:else>
青春年華,無限風光!
</s:else>
</body>
效果:
OGNL表示式可以使用in和not in兩個元素符號,in表示式用來判斷某個元素是否在指定的集合物件中,not in判斷某個元素是否不在某個集合物件中。
測試程式碼2:
<body>
<!-- OGNL表示式in的測試 -->
<s:if test="'liao' in {'liao','zhong','min'}">
您中大獎啦!
</s:if>
<s:else>
您被坑啦!
</s:else>
<!-- OGNL表示式not in的測試 -->
<s:if test="'liao' not in {'lavimer','hello'}">
你不在啊!
</s:if>
<s:else>
你在啊!
</s:else>
</body>
注:<s:select>下拉框、<s:checkboxlist>複選框、<s:radio>單選框在Web開發中經常使用,將在後面的文章中以專題的形式詳細討論。