1. 程式人生 > >struts2標籤庫----控制標籤詳解

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的控制標籤部分就簡單介紹到這,如有錯誤,望不吝賜教!