struts2標籤庫----控制標籤詳解
前面幾篇文章我們介紹了有關struts2的基本內容,簡單理解了整個框架的運作流程。從本篇開始我們逐漸瞭解和使用struts2為我們提供的標籤庫技術,使用這些標籤可以大大降低我們表現層的開發難度。根據這些標籤的使用途徑可以初步劃分為以下三大類:
- UI標籤:主要用於生成HTML標籤元素
- 非UI標籤:主要用獲取後臺資料,簡單的邏輯控制等
- Ajax標籤:用作js請求
對於UI標籤我們又大致可以分為兩類,表單標籤和非表單標籤。對於非UI標籤我們也是可以分為兩類,流程控制標籤和資料訪問標籤。本篇文章首先來介紹流程控制標籤的使用情況。
一、Struts2中OGNL表示式語言的使用
在介紹標籤庫技術之前,我們需要先簡單瞭解下有關OGNL表示式語言的一些相關知識,因為在我們的標籤庫使用中無時不涉及到對OGNL表示式的使用。OGNL表示式和JSP中的EL很是類似,都是用於取資料的,只是OGNL配合著Struts2標籤庫可以實現更加強大的功能。下面我們簡單瞭解下OGNL的使用:
每當我們在位址列請求一個Action的時候,我們的核心攔截器會迅速建立一個ActionContext上下文,Action例項還有一個ValueStack(值棧)。這個值棧的內部結構是:
主要有兩部分組成,一個context(OgnlContext),實際上就是一個map結構,一個是root(實際上是ArrayList),我們叫他根棧。在context中有兩部分組成,一個是Map結構的 _values,一個是 _root(和root是一樣的,這裡是將root對映到context中了)。所以我們一般只需要操作這個context就可以完成對值棧的操作了。由此我們可以知道我們要使用的OGNL的上下文(ValueStack)主要有兩部分組成:
在我們框架中,ActionContext中有兩個重要的屬性:
public static final String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
private Map<String, Object> context;
所以我們一般認為ActionContext就是OGNL上下文,其中的context就是ValueStack中的contextMap,這裡的VALUE_STACK 其實是一個完整的ValueStack物件,但是我們一般只操作他的root屬性。由上述兩者構成了OGNL的上下文。當我們使用OGNL表示式語言的時候,就會到這個上下文中查詢資料。其中訪問根棧中資料(root)是不需要使用#的,但是contextMap中的資料訪問時是需要字首#的,具體的下文介紹。
在初次載入Action例項的時候會預設將如下的一些物件新增到上述的contextMap中:
- parameters: 該 Map 中包含當前請求的請求引數
- request: 該 Map 中包含當前 request 物件中的所有屬性
- session: 該 Map 中包含當前 session 物件中的所有屬性
- application:該 Map 中包含當前 application 物件中的所有屬性
- attr: 該 Map 按如下順序來檢索某個屬性: request, session, application
有關OGNL上下文的基本結構就簡單介紹到這,稍微小結一下,OGNL的上下文主要有兩部分組成,一個是跟棧(root),一個是contextMap(這裡面預設會新增一些物件,訪問其中內容的時候是需要使用#),每個request請求會對應建立一個ValueStack,不是每個Action例項對應一個ValueStack,如果Action中redirect別的Action或者資源的時候,這個ValueStack就會被銷燬,其中有關上此Action的一些資訊會全部丟失。具體的ognl語法此處並沒有直接介紹,這些語法將會貫穿著在介紹標籤使用情況的時候詳細說明。
二、基本控制標籤
基本控制標籤主要有8個,它們是:
- property獲取屬性標籤
- if/else/else if邏輯判斷標籤
- iterator迭代標籤
- append組合集合標籤
- generator拆分字串標籤
- merge組合集合標籤(處理方式和append不一樣)
- subset獲取子集合標籤
- sort排序標籤
1、property獲取屬性值標籤
在使用struts標籤庫之前我們需要在jsp頁面引入該標籤庫:
<%@ taglib prefix="s" uri="/struts-tags" %>
.....
<s:property value=".." default=".." escape=".."/>
該標籤主要有三個屬性,value屬性指定需要輸出的物件的屬性名稱,如果沒有指定將取ValueStack(root)棧頂元素。default指定該標籤的預設值,也就是在獲取指定屬性值為空的時候預設輸出的值。escape屬性指定是否格式化為html程式碼(很少使用)。下面看幾個例項:
public class LoginAction extends ActionSupport {
public String execute() throws Exception{
//向context中新增一個map鍵值對
ActionContext.getContext().put("hello","walker");
return SUCCESS;
}
}
<html>
<head>
<title></title>
</head>
<body>
<h1>this is the index page</h1>
<s:property value="#hello"/>
//用於除錯的標籤,不用理會
<s:debug/>
</body>
</html>
value屬性中的內容就是一個ognl表示式,我們之前說過ValueStack中的context中資料的訪問是需要使用#,相反如果是root中的屬性則不需要使用#。我們點開debug,看到:
剛剛put的鍵值對是被存放在了context這個map中的,也看到了其中預設被新增的一些物件,如request,application,parameters等。需要記住的是訪問這個context的ognl表示式需要使用 #字首。
2、if/else if/else邏輯判斷標籤
這幾個標籤其實含義和我們Java SE中的if/else 差不多,只是用標籤的形式在HTML頁面使用了。if標籤主要有一個test屬性,這個屬性的值是一個boolean型別的,該標籤也就是根據這個值判斷是否輸出其中內容。具體看一個例子:
<body>
<h1>this is the index page</h1>
<s:if test="'a'=='a'">
<p>walker</p>
</s:if>
<s:debug/>
</body>
基本用法還是和JavaSE差不多,只是形式不一樣。至於else標籤一定得和if標籤組合在一起使用。至於內部原始碼是如何實現這個標籤的,還未參透,望諒解。
3、iterator迭代標籤
iterator標籤主要用於對一個給定的集合實行遍歷輸出操作,主要包含以下幾個屬性:
- value:指定將要被迭代的集合,如果沒有取棧頂元素
- id:指定當前正在遍歷的元素的標識
- status:該屬性為IteratorStatus例項,主要用於判斷當前迭代的元素的屬性
public class LoginAction extends ActionSupport {
public String execute() throws Exception{
ArrayList<String> list = new ArrayList<String>();
list.add("walker");
list.add("yam");
list.add("aaa");
list.add("bbb");
ActionContext.getContext().put("name",list);
return SUCCESS;
}
}
<body>
<h1>this is the index page</h1>
<s:iterator value="#name" id="n">
<p><s:property value="n"/></p>
</s:iterator>
<s:debug/>
</body>
我們在Action中向context中存入一個ArrayList,jsp頁面通過呼叫iterator標籤實現遍歷操作,id屬性指定了當前遍歷元素。結果如下:
當然有人說,我如果不想遍歷後臺的集合而是在jsp頁面自定義一個集合然後遍歷輸出該怎麼辦呢?這裡就要涉及到ognl表示式的一種定義集合的語法了。看例子:
<body>
<h1>this is the index page</h1>
<s:iterator value="{'walker','yam','aaa','bbb'}" id="n">
<p><s:property value="n"/></p>
</s:iterator>
<s:debug/>
</body>
ognl支援使用{……},構建一個list集合,使用#{name2:value1,name2:value2}構建map集合。上述程式碼執行結果如下:
我們開啟debug可以看到:
在context中儲存了一個鍵值對n=bbb,這說明這種方式的迭代是通過將當前遍歷的元素新增到context中然後通過property標籤立即取出來實現的,所以這裡儲存了最後一個鍵值對,前面的都被覆蓋掉了。
4、append合併集合標籤
我們往往在頁面上會得到兩個或兩個以上的集合,我們有時希望能夠合併他們然後一起迭代輸出。這裡就需要用到我們的append標籤。append標籤只需要指定一個屬性var即可,該屬性表示拼接之後生成的新集合的名稱,就版本用的是id,但是已經不推薦使用了。該標籤還需要配合param標籤一起使用,param標籤指定的就是一個子集合,具有的value屬性用於指定該子集合的內容也是個ognl表示式。例如:
<body>
<h1>this is the index page</h1>
<s:append var="mylist">
<s:param value="{'a','b','c'}"/>
<s:param value="{'d','e','f'}"/>
</s:append>
<s:iterator value="#mylist" id="n">
<p><s:property value="n"/></p>
</s:iterator>
<s:debug/>
</body>
輸出結果:
這裡我們為新集合命名為mylist,我們進debug看到:
我們的新集合被存入context中,所以我們上述使用的iterator標籤在遍歷新集合的時候是使用#訪問的,當然除了list,我們一樣可以合併map,但是在遍歷map的時候可以使用如下兩條語句分別訪問key和value。
<s:property value="key"/> //獲取當前遍歷map的key
<s:property value="value"/> //獲取當前遍歷map的value
5、generator分割字串標籤
這個標籤的功能和String的split方法是類似的。用於將指定的字串根據某種字元進行分割,返回的是一個集合。該標籤提供如下幾個屬性:
- count:該屬性指定了返回的集合中包含的元素個數,超過該值的元素將被捨棄
- separator:該屬性指定了用於分割字串的字元
- val:該屬性指定了將要被分割的字串
- var:該屬性指定了儲存在context中的名字,如果沒有指定該屬性將不會儲存在context中
<body>
<h1>this is the index page</h1>
<s:generator separator="," val="'walker,yam,c,y,y'" var="name"/>
<s:iterator value="#name" id="n">
<p><s:property value="n" /></p>
</s:iterator>
<s:debug/>
</body>
上述程式碼中我們使用generator標籤將字串“walker,yam,c,y,y”作為引數傳入val屬性中,並指定拆分結果儲存到context中,然後我們遍歷了這個集合結果如下:
還有一個細節需要注意下,在generator標籤開始的時候會將分割結果儲存在root中,標籤結束的時候會從root中移除。例如:
<body>
<h1>this is the index page</h1>
//標籤開始
<s:generator separator="," val="'walker,yam,c,y,y'">
<s:iterator id="n">
<p><s:property value="n"/> </p>
</s:iterator>
</s:generator>//標籤結束
<s:debug/>
</body>
我們看到在使用generator標籤的時候並沒有指定它儲存到context中,並且在使用iterator標籤的時候也沒有指定需要遍歷的集合,自然從root棧頂獲取一個元素遍歷,這個集合就是generator標籤開始時將結果壓入的集合,親測正確。告訴我們的是,在generator標籤中結果集合是被壓入棧頂的,可以不用#來訪問。
6、merge標籤拼接集合
之前已經介紹過了,該標籤和append標籤的區別在於他們拼接的方式不一樣,append是將後一個集合新增到前一個的尾部,而merge不一樣。我們看個例子:
<body>
<h1>this is the index page</h1>
<s:merge var="mylist">
<s:param value="{'walker','yam'}"/>
<s:param value="{'c','y','y'}"/>
</s:merge>
<s:iterator value="#mylist">
<p><s:property/></p>
</s:iterator>
<s:debug/>
</body>
拼接方式不同,其他沒什麼不同。此處不再贅述。
7、subset獲取子集標籤
該標籤主要用於對某個集合中的元素進行篩選過濾,獲取子集的作用。它主要有如下屬性:
- count:該屬性指定了返回的集合中包含的元素個數,超過該值的元素將被捨棄
- source:指定將要被獲取子集的集合
- start:指定了操作從某個索引位置開始
- decider:指定獲取的方式,可以自定義
- var:如果指定該屬性,會將結果儲存在page範圍內,和之前有所區別
和generator一樣,在標籤中會將結果集合儲存在root棧頂,在標籤尾部會從棧頂移除該集合。例如:
<body>
<h1>this is the index page</h1>
<s:subset source="{'walker','yam','a','xf','yddd'}" start="1">
<s:iterator>
<P><s:property/></P>
</s:iterator>
</s:subset>
<s:debug/>
</body>
上述程式碼並沒有提供decider,所以對其中的每一個元素並沒有做任何的過濾。下面我們自定義一個decider實現我們自己的過濾集合操作。
//自定義decider繼承SubsetIteratorFilter.Decider
public class MyDecider implements org.apache.struts2.util.SubsetIteratorFilter.Decider {
public boolean decide(Object el) throws Exception{
String s = (String)el;
return s.length()>3;
}
}
<body>
<h1>this is the index page</h1>
//使用bean標籤建立類的例項並儲存到context中
<s:bean name="MyPackage.MyDecider" var="mydecider"/>
//引用mydecider例項
<s:subset source="{'walker','yam','a','xf','yddd'}" decider="#mydecider">
<s:iterator>
<P><s:property/></P>
</s:iterator>
</s:subset>
<s:debug/>
</body>
輸出結果如下:
將集合中所有長度大於三的元素抽取出來,並輸出。
8、sort排序標籤
最後我們看看排序標籤,該標籤具有以下屬性:
- conparator:該屬性指定了排序規則
- source:原集合
- var:是否儲存到page範圍內中,不是放在context中,和之前的有所不同
和之前的generator,subset一樣,標籤的開始會將結果存放到棧頂,結束時或移除。對於排序規則,我們只需要自定義一個類繼承java.util.Conparator即可。看例子:
<body>
<h1>this is the index page</h1>
//載入例項,儲存到context中
<s:bean name="MyPackage.MyComparator" var="mycon"/>
//引用例項,自定義比較規則
<s:sort comparator="#mycon" source="{'walker','yam','cyy','fasdf','a'}">
<s:iterator>
<p><s:property/></p>
</s:iterator>
</s:sort>
<s:debug/>
</body>
輸出結果如下:
輸出結果是符合我們自定義的比較規則的。
有關struts2的控制標籤部分就簡單介紹到這,如有錯誤,望不吝賜教!