天天看點

Struts2标簽庫和OGNL

學習内容

 Struts 2标簽庫

 OGNL

能力目标

 熟練使用Struts 2常用标簽

 熟練使用OGNL

本章簡介

前面我們編寫的案例使用的都是HTML标簽,這些标簽并不能很好的與Struts 2架構結合,Struts 2和Struts 1一樣都自帶了功能強大、成熟完善的标簽庫供我們使用,通過使用這些标簽庫可以大大提高開發效率和代碼的可維護性。

其實Struts 2的标簽庫是建立在OGNL基礎上的,OGNL是一種功能強大的表達式語言,通過簡單的文法即可存取對象的屬性、調用對象的方法、周遊等功能。

本章将學習OGNL和Struts 2标簽庫,包括資料通路标簽、控制标簽

核心技能部分

6.1 OGNL

6.1.1 ActionContext

Struts 2中的ActionContext、ValueStack和StackContext對象與OGNL密切相關,了解這三個對象時學習OGNL的前提。

在前面的學習中,我們已經在使用ActionContext、ValueStack和StackContext對象了,這三個對象的作用及其之間的關系如圖6.1.1所示。

Struts2标簽庫和OGNL

圖6.1.1 ActionContext

1. ActionContext對象

ActionContext(com.opensymphony.xwork.ActionContext)是Action執行時的上下文。我們可以把上下文看作是一個容器,用于存放Action執行時需要使用的對象資訊,例如請求的參數(Parameter)、會話(Session)、Servlet上下文(ServletContext)等。在每次請求執行Action之前都會建立新的ActionContext。儲存到ActionContext對象中的資料可以通過Struts2标簽和OGNL表達式語言進行通路。

2. StackContext對象

StackContext對象是ActionContext上下文中的一個類型為java.utiI.Stack的執行個體。上面提到的請求參數(Parameter)、會話(Session)、Servlet上下文(ServletContext)等對象,實際上都存儲在ActionContext上下文中的StackContext對象中。

3. ValueStack對象

Struts2會為每一次Action的請求建立與之對應的ValueStack,并将所有Action屬性資料存儲到ValueStack中,再将ValueStack 暴露給視圖頁面,這樣頁面就可以直接通路Action中的屬性值。

6.1.2 使用OGNL

OGNL即(Object-Graph Navigation Language),是對象圖導航語言的縮寫,它是一個開源項目,工作在視圖層,用來取代頁面中的Java腳本,簡化資料通路操作。

OGNL是一種比EL功能更強大的表達式語言。通過其簡單一緻的表達式文法,可以存取對象的屬性、調用對象的方法、周遊以及進行類型轉換等功能。Struts 2架構預設使用OGNL作為表達式語言。

1. 通路上下文中的資料

ActionContext是Struts 2中OGNL的上下文環境,即ActionContext中的資料(實際存儲在StackContext中)都可以通過OGNL來通路。而ActionContext又包括Request、Session、ValueStack等對象,是以通過OGNL也可以通路存儲在這些對象中的資料。

ValueStack是OGNL的根,同時由于OGNL上下文中的根對象可以直接通路,是以通過OGNL通路ValueStack中的資料時,不需要使用任何特殊的标記,而通路ActionConext中的其他對象時則需要使用“#”标記。Struts 2中提供了<s:property>标簽來顯示OGNL上下文中的資料,在實際應用中通常分為以下四種情況:

(1)<s:property  value=”uid”/>: Struts2會在ValueStack中查找名為uid的項,并輸出其值。

(2)<s:property  value=”#uid”/>: Struts2會在StackContext中查找名為uid的項,并輸出其值。

(3)<s:property  value=”#session.uid”/>: Struts2會在StackContcxt中的Session作用域中查找名為uid的項,并輸出其值。這裡的session也可以更換為request和application,以便在Request和Application作用域中查找指定的項。

(4) <s:property  value =”#attr.uid”/>: Struts2會依次在StackContext的Page、 Request、Session、Application作用域内查找對應的uid中的值。

2. 通路JavaBean屬性

通過OGNL可以非常友善的通路JavaBean,假設現在ValueStack中存在StudentAction,該Action的屬性有name、age等。

(1) <s:property value=”name”/>:Struts2會執行StudentAction對象的getName()方法來獲得并輸出name屬性的值

(2) <s:property value=”age”/>:Struts2會執行StudentAction對象的getAge()方法來獲得并輸出age屬性的值

由于ValueStack是OGNL的根,是以此時在使用<s:property>标簽時無需“#”标記。

3.  執行JavaBean方法

OGNL不僅可以自動執行getter方法,而且能夠執行JavaBean的其他方法。更重要的是,還能夠在執行方法的時候傳入參數,OGNL執行的方法可以是靜态方法和非靜态方法。執行靜态方法的格式為:@類的全類名@方法名(參數)。為了能夠讓OGNL執行靜态方法,還必須在struts.xml配置檔案中配置常量參數,才可以開啟OGNL調用靜态方法的功能,配置代碼如下:

<constantname="struts.ognl.allowStaticMethodAccess" value="true" />

示例6.1示範了使用OGNL執行JavaBean方法。

示例6.1

(1)建立JavaBean ,命名為“OgnlInfo.java”。

public class OgnlInfo { 
//定義靜态方法
public static String sayBye(String name)
{
return name + "再見!";
}
//定義非靜态方法
public String sayHello(String name)
{
return name + "你好!";
}
}           

(2)建立index.jsp。

<body>
    <%
    	//執行個體化ognl對象
    	OgnlInfo ognlInfo = new OgnlInfo();
    	//将ognlinfo對象存入request作用域内
    	request.setAttribute("ognlInfo",ognlInfo);  
    
    
    %>
    <!-- 執行ognl對象非靜态方法 -->
    <s:property value="#request.ognlInfo.sayHello('jack')"/>
    <!-- 執行ognl對象靜态方法 -->
    <s:property value="@com.zzab.entity.OgnlInfo@sayBye('tom')"/>
 
  </body>           

4.   OGNL操作符

OGNL表達式中能使用的操作符基本與Java中的操作符相同,除了能使用+、—、*、/、==、!=、=等操作符之外,還能使用mod(取餘)、in、not in等操作符。

5. “%”号

“%”号用于對屬性為字元串類型的OGNL表達式的值進行計算,“%”符号在OGNL中的使用方法如下:

<body>

<%

String value="确定";

pageContext.setAttribute("val", value);

%>

不使用%符号,會按原樣顯示<br>

<s:submit value="#attr.val"/>

<br>

使用%符号,會計算OGNL表達式的值<br>

<s:submit value="%{#attr.val}"/>

</body>

運作結果如圖6.1.2所示。

Struts2标簽庫和OGNL

圖6.1.2 %的使用

6. OGNL的集合操作

(1)生成List類型的集合。具體文法為{e1,e2,e3,...}。此文法将直接生成一個list類型集合,該集合包含3個元素:e1,e2,e3。如果需要更多的元素,則多個元素之間以英文逗号隔開。

(2)生成Map類型的集合。具體文法為#{key1,key2,key3...}。此文法将直接生成一個Map類型集合,該Map中每個key-value對象之間以英文冒号隔開,如果需要更多的元素,則多個元素之間以英文逗号隔開。

(3)使用in和not in 運算符。對于集合,OGNL提供元素符in與not in。其中,in用于判斷某個元素是否在指定集合内;not in 則用于判斷某個元素是否不在指定的集合中。In和 not in 的用法如下:

<!-- 判斷java是否在指定集合中-->

<s:if test="'java' in {'java','jsp','html'}">

存在

<s:else>

不存在

</s:else>

</s:if>

(4)   擷取集合的子集。OGNL允許通過某個規則取得集合的子集,取得子集時有以下三種操作符:

1)“?”操作符:取出所有符合選擇邏輯的元素。

2)“^”操作符:取出符合選擇邏輯的第一個元素。

3)“$”操作符:取出符合選擇邏輯的最後一個元素。

假設現在Session中存在名為StudentList的集合,該集合存儲了多個Student對象。Student類中存在一個age屬性,這三種操作的文法如下:

#session.StudentList.{?#this.age>20}用于取出年齡大于20的子集合。

#session.StudentList.{^#this.age>20}用于取出第一個年齡大于20的Student對象。

#session.StudentList.{$#this.age>20}用于取出最後一個年齡大于20的Student對象。

6.2 Struts 2的标簽庫

Struts 2作為一個優秀的MVC架構,它為開發人員提供了一個功能強大、支援廣泛、高擴充性的标簽庫,完全能滿足Web頁面複雜、多變的需求。

Struts 2的标簽庫大大簡化了視圖頁面的代碼編輯工作,提高了視圖頁面的維護效率。與Struts 1的标簽庫相比,Struts 2的标簽庫不但功能強大,而且更加簡單易用。

在Struts 1的标簽庫中,它分别提供了html、bean、logic等幾個标簽庫。Struts 2标簽庫并沒有嚴格的對标簽進行分類,而是把所有标簽整合到了一個标簽庫中。為了使初學者對Struts 2标簽庫能有更加清晰的認識,我們可以将Struts 2的标簽庫分成三大類(如圖6.1.3所示):

 UI标簽:主要用于生成HTML頁面元素。

 非UI标簽:主要用于邏輯控制、資料通路等操作。

 Ajax标簽:主要用于支援Ajax通路。

其中又可以把UI标簽分為如下兩類:

表單标簽:主要用于生成HTML中的表單元素。

 非表單标簽:主要包含一些常用的功能标簽,例如顯示日期或樹形菜單。

對于非UI标簽,也可以再細分為如下兩類:

 資料通路标簽:主要用于獲得或輸出ValueStack中的值。

 邏輯控制标簽:主要用于資料通路中的條件分支、循環等流程控制。

Struts2标簽庫和OGNL

圖6.1.3 标簽庫分類

使用Struts2标簽庫之前,也需要在JSP頁面中導入。現在的Web應用一般都使用Servlet 2.4以上的規範,是以我們就無須在web.xml檔案中對标簽庫進行配置,因為Servlet 2.4以上的規範可以直接讀取标簽庫定義檔案(TLD檔案),在JSP頁面中直接通過下面一行代碼就可以導入Struts 2标簽庫。

<%@ taglib prefix="s" uri="/struts-tags" %>

6.2.1 資料通路标簽

資料通路标簽主要用來獲得或輸出各種資料的功能,常應用于顯示Action中的屬性以及國際化輸出等場合。常見的資料标簽主要包含以下幾個:

 action标簽:該标簽用于在JSP頁面中直接調用一個Action 。

include标簽:該标簽用于在JSP頁面中包含其他頁面資源。

 param标簽:該标簽用于設定一個參數,通常用作bean、url标簽的子标簽。

 property标簽:該标簽用于輸出某個值。

 set标簽:該标簽用于設定一個新變量。

 text标簽:該标簽用于輸出國際化消息。

 url标簽:該标簽用于生成一個URL位址。

 date标簽:該标簽用于格式化輸出一個日期。

1. action标簽

該标簽可以在JSP頁面中直接調用Action,并可以把執行的結果包含在标簽的位置,這時需要指定Action的name和namespace等屬性。下面是該标簽的幾個主要屬性:

 id屬性:該屬性用來指定被調用的Action的引用ID。

 name屬性:該屬性用來指定被調用的Action的名字。

 namespace屬性:該屬性用來指定被調用的Action所在的namespace。

 executeResult屬性:該屬性用來指定是否将Action的處理結果包含到目前頁面中,該

屬性的預設值是false,即不包含。

示例6.2

下面我們通過一個執行個體來介紹action标簽的具體應用,首先編寫一個簡單的Action類,代碼如下所示。

public class TagAction

{

private String name;

//省略getter和setter方法

public String exec()

return "ok";

}

通過上述代碼可以看到該Action隻包含了一個exec業務方法。下面是該Action在struts.xml檔案中的配置:

<struts>
    <package name="struts2_Tags" extends="struts-default"
     namespace="/tags">      
<action name="tag1" class="com.zy.TagAction" method="exec">
<result name="ok">succ.jsp</result>
</action>
    </package>
</struts>           

下面是succ.jsp視圖檔案的代碼:

調用Action!!

下面是使用action标簽的s-action_Tag.jsp檔案,代碼如下所示:

下面調用Action,并将結果包含到本頁面中。<br>

<s:action name="tag1" executeResult="true" namespace="/tags"/>

上述代碼使用<s:action />标簽調用了名字為tag1的邏輯action,并通過設定executeResult屬性為true将結果包含到了本頁面中。

Struts2标簽庫和OGNL

圖6.1.4  action标簽的應用

2. property标簽

該标簽的作用就是輸出ActionContext中對象的值或者Action、JavaBean的屬性值,通常結合OGNL使用。該标簽有如下幾個屬性:

Ø value,該屬性用來指定要輸出的屬性或OGNL表達式。

Ø default,該屬性用來指定當value指定的屬性值為null時輸出的值。

Ø escape,該屬性用來指定是否生成HTML代碼,預設為true,表示在浏覽器上輸出表達式的值;如果為false,則把表達式的值作為HTML代碼嵌入。

示例6.3

下面是應用property标簽的簡單示例。

    <%

     String url="<a href='index.jsp'>超連結</a>";

     pageContext.setAttribute("su",url);

     %>

     <s:property value="#attr.su"/><hr>

     <s:property value="#attr.su" escape="false"/><hr>

     <s:property value="#attr.uid" default="找不到uid"/>

運作效果如圖6.1.5所示。

Struts2标簽庫和OGNL

圖6.1.5 property标簽

3. include标簽

該标簽用來将一個JSP或Servlet等資源包含到目前頁面中,使用起來非常簡單。該标簽主要包含value屬性,用來指定被包含的JSP或Servlet等資源檔案。

示例6.4

接下來通過一個簡單的例子來介紹include标簽的具體應用,首先是被包含的include-file.jsp頁面的代碼:

<%@ page contentType="text/html; charset=GBK" language="java"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
</head>
<body>
---------------------------
<h3>這是被包含的頁面</h3>
</body>
</html>
下面是包含include-file.jsp這個頁面的s-include_Tag.jsp檔案,代碼如下所示:
<%@ page contentType="text/html; charset=utf-8" language="java"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>使用include标簽來包含目标頁面</title>
</head>
<body>
<h2>使用include标簽來包含目标頁面</h2>
<s:include value="include-file.jsp"/>
</body>
</html>           

上述代碼通過使用include标簽包含了include-file.jsp頁面,運作效果如圖6.1.6所示。

Struts2标簽庫和OGNL

圖6.1.6 include标簽

4. set标簽

該标簽用來定義一個變量,并把這個變量放到指定的範圍内,例如application範圍、session範圍等。該标簽有以下一些常用屬性:

Ø id屬性:該屬性用來定義變量的名字。

Ø scope屬性:該屬性用來定義變量被放置的範圍,可選的值有application、session、request、response、page和StackContext。

Ø value屬性:該屬性用來定義将要賦給變量的固定值或者另一個已經定義的變量。

示例6.5

下面我們通過一個示例來介紹set标簽的具體應用,首先我們定義一個變量p,并把“小樣兒”這個字元串指派給它,然後定義另一個變量user,并把p指派給user。代碼如下所示:

<h2>使用set标簽設定一個新變量</h2>

<s:set id="p" value="'小樣兒'"/>

<s:property value="#attr.p"/><br>

将p變量指派給user變量并放入session範圍内<br>

<s:set value="#p" id="user" scope="session"/>

<s:property value="#session.user"/><br>

運作效果如圖6.1.7所示。

Struts2标簽庫和OGNL

圖6.1.7 set标簽的應用

5. url标簽

該标簽用來生成一個URL位址,我們也可以通過嵌套param标簽來為URL指定發送參數。該标簽主要有以下一些常用屬性:

Ø includeParams屬性:該屬性用來指定是否在生成的URL參數中追加本頁面接收到的請求參數,有3個值可供選擇,none, get和all,分别代表“不包含”、“包含get方法傳遞的參數”和“所有的參數”。

Ø value屬性:該屬性用來指定要生成的URL的位址值。

Ø action屬性:該屬性用來指定一個Action作為URL位址值。

Ø namespace屬性:該屬性用來指定命名空間。

Ø method屬性:該屬性用來指定調用Action的方法名。

Ø includeContext屬性:該屬性用來指定是否把目前上下文包含在URL位址值中。

在上述屬性中,action和value屬性隻需指定其一即可,如果兩個都未指定,就以目前頁面作為URL位址值。下面是一個應用URL标簽的示例。

示例6.6

<body>
<h3>使用url标簽</h3>
<hr>
指定action屬性,且使用param标簽傳入參數。<br>
<s:url action="showPerson">
    <s:param name="name" value="'Jack'" />
</s:url>
<hr>
指定value屬性,且使用param标簽傳入參數。<br>
<s:url value="http://localhost:8080/firstStruts2/news.jsp">
    <s:param name="name" value="'Jack'" />
</s:url>
</body>           

該頁面的運作效果如圖6.1.8所示。

Struts2标簽庫和OGNL

圖6.1.8 URL标簽的應用

6. date标簽

date标簽用來按指定的格式輸出一個日期,還可以計算指定日期到目前時間的時差。該标簽主要有如下一些屬性:

Ø format屬性:該屬性用來指定日期輸出的格式,例如“yyyy-MM-dd”。

Ø name屬性:該屬性用來指定要被格式化輸出的日期值。

示例6.7

我們看下面的執行個體代碼,用“年/月/日”的完整格式顯示目前日期:

<body>
<h3>date标簽的使用</h3>
<%
java.util.Date now = new java.util.Date();
pageContext.setAttribute("now" , now);
%>
<s:date name="#attr.now" format="yyyy/MM/dd" nice="false"/><hr><br>
</body>           

該頁面的運作效果如圖6.1.9所示。

Struts2标簽庫和OGNL

圖6.1.9 date标簽的使用

7. <s:bean>标簽

<s:bean>用來建立JavaBean執行個體,建立JavaBean執行個體的時候,可以在該标簽内部使用<param/>标簽為該JavaBean執行個體傳入屬性。其常見屬性如下:

Ø Id:指定建立JavaBean的引用id.

Ø name:指定需要執行個體化的JavaBean的實作類

<s:bean>标簽用法的如下:

(1) 建立JavaBean,命名為Book.java。

public class Book { 
private String isbn;
private String name;
private double price;
public Book() {
super();
}
public Book(String isbn, String name, double price) {
super();
this.isbn = isbn;
this.name = name;
this.price = price;
}
public String[] getAllBooks()
{
String[] books = {"JAVA","JSP","ASP.NET","C#"};
return books;
}
}           

(2) 建立Jsp頁面.

<!-- 執行個體化book JavaBean對象,并存儲到StackContext中 -->

<s:bean id="book" name="com.zzab.entity.Book">

<s:param name="isbn" value="'621345'"/>

<s:param name="name" value="'HTML'"/>

</s:bean>

<!-- 輸出bean對象的值 -->

book對象屬性值:<br>

isbn:<s:property value="#book.isbn"/><br/>

name:<s:property value="#book.name"/><br/>

調用book對象的getAllBooks方法擷取全部書籍清單:<br/>

<c:forEach var="book" items="${book.allBooks}">

<div>${book }</div>

</c:forEach>

<s:bean id="book" name="com.zzab.entity.Book">
<s:param name="isbn" value="'621345'"/>
<s:param name="name" value="'HTML'"/>
</s:bean>
<!-- 輸出bean對象的值 -->
book對象屬性值:<br>
isbn:<s:property value="#book.isbn"/><br/>
name:<s:property value="#book.name"/><br/>
調用book對象的getAllBooks方法擷取全部書籍清單:<br/>
<c:forEach var="book" items="${book.allBooks}">
<div>${book }</div>
</c:forEach>           

6.2.2 邏輯控制标簽

Struts 2的邏輯控制标簽主要用于程式流程的邏輯控制,例如選擇、分支、循環等操作,常見的邏輯控制标簽主要有以下五個标簽:

Ø if标簽:用于控制選擇流程的标簽。

Ø elseif标簽:與if标簽結合使用。

Ø else标簽:與if标簽結合使用。

Ø generator标簽,用于将一個字元串解析成一個集合。

Ø iterator标簽:這是一個疊代器,用于将集合進行循環輸出。

1. if/elseif/else标簽

if/elseif/else這三個标簽通常結合使用,都是用于進行程式分支邏輯控制,跟多數程式設計語言中的if/elseif/else語句的功能相似,test屬性用來設定比較表達式,具體文法如下所示:

<s:if test="表達式">

标簽體

<s:elseif test="表達式">

下面是一個執行個體的代碼片段:

示例6.8

<s:set name="dengji" value="85"/>
<s:if test="#dengji>=90">
    成績優秀
</s:if>
<s:elseif test="#dengji>=80">
    成績良好
</s:elseif>
<s:elseif test="#dengji>=60">
成績合格
</s:elseif>
<s:else>
    成績不合格
</s:else>           

上述代碼先通過set标簽定義了一個名為dengji的屬性并指派為85,然後通過if/elseif/else标簽根據dengji屬性的值的範圍來控制輸出成績的評價是“優秀”、“良好”、“合格”或“不合格”。運作效果如圖6.1.10所示。

Struts2标簽庫和OGNL

圖6.1.10 if/elseif/else标簽的應用

2. iterator标簽

該标簽主要用于對集合類型的變量進行疊代輸出,這裡的集合類型包括List、Set、數組和Map等。該标簽主要有三個屬性:

Ø value屬性:該屬性用來指定被疊代輸出的集合,被疊代的集合可以由OGNL表達式指

定也可以通過Action傳回一個集合類型的變量。

Ø id屬性:該屬性用來指定集合中元素的ID屬性。

Ø status屬性:該屬性可以實作一些附加功能,詳見後面講解。

示例6.9

<h3>iterator的用法</h3>

 <%

  List bookList=new ArrayList();

  bookList.add("Spring3.0技術寶典");

  bookList.add("Struts2權威指南");

  bookList.add("JavaScript技術大全");

  bookList.add("JSP技術詳解");

  bookList.add("Ajax實戰");

  pageContext.setAttribute("bookList",bookList);

  %>

 <s:iterator value="#attr.bookList" id="bookname">

 <li><s:property value="bookname" /></li>

 </s:iterator>

上述代碼通過iterator标簽的value屬性指定了一個集合,并指定它的id屬性為bookname,然後就可以通過<s:property value="bookname" />輸出集合中每個元素的值。該頁面的運作效果如圖6.1.11所示。

Struts2标簽庫和OGNL

圖6.1.11 iterator标簽的應用

iterator标簽的status屬性很有用,利用它可以實作一些很實用的功能。指定該屬性後,每次疊代都會以此屬性為名稱建立一個IteratorStatus的執行個體對象,該執行個體對象包含了以下幾個屬性:

Ø count:傳回目前疊代了幾個元素。

Ø index:傳回目前疊代元素在集合中的索引值。

Ø even:傳回目前疊代元素的索引值是否為偶數。

Ø odd:傳回目前疊代元素的索引值是否為奇數。

Ø first:傳回目前疊代元素是否是第一個元素。

Ø last:傳回目前疊代元素是否是最後一個元素。

我們看下面的代碼片段:

<table border="1" width="200">

 <s:iterator value="#attr.bookList" id="name" status="st">

<tr <s:if test="#st.odd">style="background-color:#bbbbbb"</s:if>>

<td><s:property value="#st.index"/></td>

<td><s:property value="name"/></td>

</tr>

</table>

上述代碼以表格的形式輸出了集合中的每個元素和元素的索引值,并通過iterator标簽的status屬性控制奇數行的背景色,運作效果如圖6.1.12所示。

Struts2标簽庫和OGNL

圖6.1.12 status屬性的應用

3. generator标簽

該标簽可以将一個字元串按指定的分隔符分隔成多個子串,新生成的多個子串可以使用iterator标簽進行疊代輸出。generator标簽常見的屬性如下:

Ø count屬性:該屬性用來指定所生成集合中元素的總數。

Ø separator屬性:該屬性用來指定分隔符。

Ø converter屬性:該屬性指定了一個轉換器,該轉換器負責将集合中的每個字元串轉換成

對象。

Ø id屬性:如果指定了該屬性,則新生成的集合會以此為名稱,被放在pageContext中。

我們來看下面的代碼片段:

示例6.10

<s:generator val="'Spring技術寶典,Ajax實戰,Struts2權威指南,JSP技術詳解,

JavaScript技術大全'" separator="," id="generList"/>

<table border="1" width="320">

<caption>疊代輸出由generator标簽産生的集合</caption>

 <s:iterator value="#attr.generList" id="name" status="st">

<tr>

上述代碼使用generator标簽把一個字元串按逗号進行了分割,并把産生的集合按照指定的“generList”為id儲存在pageContext中,接下來使用iterator标簽疊代輸出generList集合中的元素,運作效果如圖6.1.13所示。

Struts2标簽庫和OGNL

圖6.1.13 generator标簽的應用

本章總結

Ø ActionContext對象。ActionContext(com.opensymphony.xwork.ActionContext)是Action執行時的上下文。我們可以把上下文看作是一個容器,用于存放Action執行時需要使用的對象資訊。

Ø StackContext對象。請求參數(Parameter)、會話(Session)、Servlet上下文(ServletContext)等對象,實際上都存儲在ActionContext上下文中的StackContext對象中。

Ø ValueStack對象。Struts2會為每一次Action的請求建立與之對應的ValueStack,并将所有Action屬性資料存儲到ValueStack中,再将ValueStack 暴露給視圖頁面,這樣頁面就可以直接通路Action中的屬性值。

Ø OGNL即(Object-Graph Navigation Language),是對象圖導航語言的縮寫,它是一個開源項目,工作在視圖層,用來取代頁面中的Java腳本,簡化資料通路操作。

(1)通路上下文中的資料。ValueStack是OGNL的根,同時由于OGNL上下文中的根對象可以直接通路,是以通過OGNL通路ValueStack中的資料時,不需要使用任何特殊的标記,而通路ActionConext中的其他對象時則需要使用“#”标記。

(2)執行JavaBean方法。OGNL不僅可以自動執行getter方法,而且能夠執行JavaBean的其他方法。更重要的是,還能夠在執行方法的時候傳入參數,OGNL執行的方法可以是靜态方法和非靜态方法。

Ø Struts 2的标簽庫

(1)Struts2标簽庫的分類。我們可以将Struts 2的标簽庫分成三大類:UI、非UI、ajax标簽,其中UI标簽又可分為表單 、非表單标簽,非UI标簽可分為資料通路、邏輯控制标簽。

(2)資料通路标簽。常見的資料标簽主要包含action、include、param 、property、set、text、url、date等。

(3)邏輯控制标簽。邏輯控制标簽主要用于程式流程的邏輯控制,例如選擇、分支、循環等操作,常見的邏輯控制标簽主要有五個标簽:if 、elseif、 else 、generator 、iterator。

任務實訓部分

1:查詢管理者

訓練技能點

Ø OGNL

Ø property标簽

Ø iterator标簽

  需求說明

在前面章節的任務實訓部分,我們實作了管理者的增、删、改、查等功能,現在要求在查詢管理者時,使用OGNL和iterator、property等标簽顯示查詢到的管理者清單,并且隻顯示密碼長度小于3的管理者資訊。

  關鍵代碼

<s:iterator value="#attr.adminList.{?#this.pwd.length()>3}" id="admin">

   <tr>

     <td align="center"><s:property value="id"/></td>

     <td align="center"><s:property value="name"/></td>

     <td align="center"><s:property value="pwd"/></td>

   </tr>

</s:iterator>

2:查詢圖書

在前面章節的任務實訓部分,我們實作了查詢圖書的案例,現在要求使用OGNL和iterator、property等标簽顯示查詢到的圖書清單,并且設定偶數行的背景色為灰色。

3:查找客戶資訊

Ø 資料通路标簽

Ø 控制标簽

實作輸入客戶編号時,查找對應的客戶是否存在,若不存在,則輸出無法查找到此客戶的提示;若存在,則進一步顯示客戶的訂單資訊。

  實作思路

(1) 建立用于描述訂單的實體類,命名為Order.java。

public class Order implements Serializable{

//訂單Id

private String orderId;

//訂單日期

private Date orderDtm;

//訂單狀态 0:禁用 1:啟用

private int state;

//無參構造函數

public Order() {

super();

// TODO Auto-generated constructor stub

//有參構造函數

public Order(String orderId, Date orderDtm, int state) {

this.orderId = orderId;

this.orderDtm = orderDtm;

this.state = state;

//getter setter省略......

(2) 建立用于描述客戶的實體類,命名為Cust.java。

public class Cust implements Serializable{

//客戶編号

private String custId;

//客戶名稱

private String custName;

//電話

private String tel;

//訂單集合

private List<Order> orders = new ArrayList<Order>();

public Cust() {

public Cust(String custId, String custName, String tel) {

this.custId = custId;

this.custName = custName;

this.tel = tel;

(3) 建立用于處理業務邏輯的JavaBean,命名為CustService.java。

public class CustService {

//定義客戶清單

private static List<Cust> custList = new ArrayList<Cust>();

//靜态構造器,初始化客戶和訂單資訊

static{

try {

//用于格式化日期

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

//建立客戶c1

Cust c1 = new Cust("c001", "張三", "110");

Order c1Order1  = new Order("OR001", sdf.parse("2012-01-01"), 0);

Order c1Order2  = new Order("OR002", sdf.parse("2012-01-02"), 0);

Order c1Order3  = new Order("OR003", sdf.parse("2012-01-03"), 1);

//添加訂單到c1客戶

c1.getOrders().add(c1Order1);

c1.getOrders().add(c1Order2);

c1.getOrders().add(c1Order3);

//建立客戶c2

Cust c2 = new Cust("c002", "李四", "114");

Order c2Order1  = new Order("OR004", sdf.parse("2012-01-04"), 0);

Order c2Order2  = new Order("OR005", sdf.parse("2012-01-05"), 0);

Order c2Order3  = new Order("OR006", sdf.parse("2012-01-06"), 1);

//添加訂單到c2客戶

c2.getOrders().add(c2Order1);

c2.getOrders().add(c2Order2);

c2.getOrders().add(c2Order3);

//建立客戶c3

Cust c3 = new Cust("c003", "王五", "119");

Order c3Order1  = new Order("OR007", sdf.parse("2012-01-07"), 0);

Order c3Order2  = new Order("OR008", sdf.parse("2012-01-08"), 0);

Order c3Order3  = new Order("OR009", sdf.parse("2012-01-09"), 1);

//添加訂單到c3客戶

c3.getOrders().add(c3Order1);

c3.getOrders().add(c3Order2);

c3.getOrders().add(c3Order3);

//把客戶添加到List清單中

custList.add(c1);

custList.add(c2);

custList.add(c3);

} catch (ParseException e) {

// TODO Auto-generated catch block

e.printStackTrace();

//

public static Cust findCustById(String custId){

//循環每個客戶

for (Cust cust : custList) {

if(cust.getCustId().equals(custId)){

return cust;

//如果找不到則傳回空

return null;

public static List<Order> getCustOrders(String custId){

//傳回對應客戶的訂單

return cust.getOrders();

(4) 建立用于處理業務邏輯的控制器Action,命名為CustAction.java。

public class CustAction extends ActionSupport {

//封裝使用者的id

//查找訂單

public String findOrder()throws Exception{

//調用業務層

List<Order> orderList = CustService.getCustOrders(custId);

//将訂單放到request中

ServletActionContext.getRequest().setAttribute("orderList", orderList);

//傳回input邏輯視圖

return SUCCESS;

/**

 * @return

 */

public String findCust() {

// TODO Auto-generated method stub

//調用業務邏輯

Cust cust = CustService.findCustById(custId);

if(cust!=null){

//将cust存儲到session中

ServletActionContext.getRequest().getSession().setAttribute("cust", cust);

else{

//否則給msg指派,準備彈出對話框。

ServletActionContext.getRequest().setAttribute("msg","<script>alert('沒有此使用者');</script>");

//getter setter方法

public String getCustId() {

return custId;

public void setCustId(String custId) {

(5) 建立Jsp頁面,命名為jd1.jsp。

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <title>My JSP 'jd1.jsp' starting page</title>

  </head>

  <body>

    <h2>

    <form action="custaction!findCust.action" method="post">

    完成客戶查找功能:<hr>

    客戶編号:<input type="text" name="custId"> <br/>

    <input type="submit" value="查找">

    <!-- 如果找到該使用者 -->

    <s:if test="#session.cust">

    <hr/>

    編号:${cust.custId}<br/>

    姓名:${cust.custName}<br/>

    聯系方式:${cust.tel}<br/><a href="custaction!findOrder.action?custId=${cust.custId }">檢視訂單</a>

    </s:if>

    <!-- 如果存在訂單清單 -->

    <s:if test="#request.orderList">

    <table border="1">

    <tr>

    <td>訂單編号</td>

    <td>訂單日期</td>

    <td>訂單狀态</td>

    </tr>

    <s:iterator id="row" value="#request.orderList">

    <td>${row.orderId }</td>

    <td><s:date name="#row.orderDtm" format="yyyy年MM月dd日"/></td>

    <td>${row.state==0?"禁用":"啟用" }</td>

    </s:iterator>

    </table>

    </form>

    </h2>

    <!-- 執行msg -->

    ${msg }

  </body>

</html>

(6) 配置Struts.xml檔案。

<struts>

<!-- 配置字元集 -->

<constant name="struts.i18n.encoding" value="utf-8"></constant>

<!-- 配置主題 -->

<constant name="struts.ui.theme" value="simple"></constant>

<package name="test" extends="struts-default">

<action name="custaction" class="com.zzzy.web.CustAction">

<result >/jd1.jsp</result>

</action>

</package>

</struts>

(7) 運作jd1.jsp頁面,效果如圖6.2.1所示。

Struts2标簽庫和OGNL

圖6.2.1 運作jd1.jsp

在圖6.2.1中輸入需要查找的客戶編号。

若編号不存在,系統将會顯示提示消息。 如圖6.2.2所示。

Struts2标簽庫和OGNL

圖6.2.2 使用者不存在

若編号存在,系統将顯示該客戶對應的詳細資訊,如圖圖6.2.3所示。

Struts2标簽庫和OGNL

圖6.2.3 查找客戶并顯示詳細資訊

在圖6.2.3中單擊“檢視訂單”按鈕,系統将顯示客戶對應的訂單資訊,如圖6.2.4所示。

Struts2标簽庫和OGNL

圖6.2.4顯示客戶的訂單資訊

鞏固練習

一、選擇題

1. 以下關于OGNL的說法正确的是()。

A. ActionContext是OGNL的上下文環境

B. ValueStack是OGNL的根

C. StackContext是OGNL的上下文環境

D. ActionContext是OGNL的根

2. 假設session中存在名為uid的屬性,通過OGNL通路該屬性的代碼是()。

A. #uid

E. uid

F. #session.uid

G. ${session.uid}

3. 關于“#session.users.{?#this.age>20}”的OGNL代碼所表示的意思是()。

A. 從users集合中取出第一個年齡大于20的對象

H. 從users集合中取出所有年齡大于20的對象

I. 從users集合中取出最後一個年齡大于20的對象

J. 該代碼有文法錯誤

4. 假設頁面中存在這樣一行代碼“<s:property />”,則下面說法正确的是()。

A. 編譯錯誤

K. 頁面不顯示任何内容

L. 輸出ValueStack中最上面的對象

M. 以上說法都不正确

5. 以下()不屬于Struts 2的資料通路标簽。

A. set