作為服務器端表示層
MVC
經典框架的
Struts
,其突出表現就是在表示層頁面流轉方面。雖然在顯示的視圖層,
Struts
框架提供了一組功能強大的標簽庫來幫助運用。但是這組標簽庫還是比較復雜,例如要取得一個
Session
中的
JavaBean
,需要做兩個步驟的動作。
(
1
)使用
<bean:define>
標簽來定義一個目標
JavaBean
的標識,并從
Session
中取得源
JavaBean
賦給目標
JavaBean
。若該
JavaBean
本身是
String
類型,則只需要設置它的
name
屬性,否則還需要設置
property
屬性。
(
2
)使用
<bean:write>
標簽將該
JavaBean
的變量屬性顯示出來。若該
JavaBean
本身是
String
類型,則只需要設置它的
name
屬性,否則還需要設置
property
屬性。
下面看一個示例,假設
Session
中有一個參數為“
TEST
”,其值為
String
類型的字符串“
hello
”。那么使用
Struts
框架的
<bean>
標簽庫的代碼就應該是這樣:
<bean:define id="test" name="TEST" scope="session"/>
<bean:write name="test"/>
定義一個目標
JavaBean
的標識“
test
”,然后將從
Session
中的參數“
TEST
”所取得的源
JavaBean
的實例賦給目標
JavaBean
。
<bean:write>
標簽會根據
<bean:define>
標簽的
id
屬性設置自身的
name
屬性,來獲取目標
JavaBean
并顯示出來。由于它們操作的是
String
類型的字符串,因此編碼還算比較簡單。可是,如果它們操作的是一個非
String
類型的
JavaBean
,那么編碼就比較麻煩了。
如果使用的是
JSTL
,這部分的操作就十分簡單了,僅僅通過
EL
表達式語言就可以完成了,轉換成
EL
表達式的操作編碼如下:
${sessionScope.TEST}
轉換成
JSTL
,只要一句表達式就已經完成了
<bean>
標簽庫需要用兩個標簽和許多屬性才能完成的工作。即使使用的是
JavaBean
中的屬性,
JSTL
表達式也只需要再加個“
.
”操作符而已。
使用
JSTL
中的
EL
表達式和
JSTL
標簽庫中的標簽,可以簡化
Struts
標簽庫中許多標簽的操作。下面就根據具體的對比來進行介紹。
Struts
的
Bean
標簽庫在
EL
表達式沒有出現前是十分常用的,無論從
Session
、
request
、
page
或是其他作用范圍(
Scope
)中取得參數、或者從標準
JavaBean
中讀取變量屬性都處理得得心應手。然而,在
EL
表達式出現之后,
Struts Bean
標簽庫的標簽在操作的時候就顯示出了煩瑣的缺點。因此用
EL
表達式來替代
Struts Bean
標簽庫中的標簽是一種較好的做法。
1. <bean:define>
標簽和
<bean:write>
標簽處理顯示被
EL
表達式替換
q????????
原形:
<bean:define>
標簽的作用是定義一個
JavaBean
類型的變量,從
Scope
源位置得到該
JavaBean
的實例。
<bean:write>
標簽可以通過
JavaBean
變量來做顯示的工作。
q????????
替換方案:利用
EL
表達式來替換。
q????????
示例比較
<bean:define>
標簽和
<bean:write>
標簽的動作:
<bean:define id="javaBeanName"
name="javaBeanParameter"
property="javaBeanProperty"
scope="request"/>
<bean:write name="javaBeanName"/>
EL
表達式的動作:
${requestScope.javaBeanParameter.javaBeanProperty}
或
${requestScope.javaBeanParameter['javaBeanProperty’]}
處理相同的一個動作,使用
define
標簽,通常需要記住各種屬性的功能,并有選擇地根據實際情況來挑選是否需要
property
屬性,還要指定其
scope
屬性。而
EL
表達式就方便多了,直接使用默認變量
pageScope
、
requestScope
、
sessionScope
、
applicationScope
指定源
JavaBean
作用范圍,利用“
.
”操作符來指定
JavaBean
的名稱以及利用“
[]
”或“
.
”來指定
JavaBean
中的變量屬性。
q????????
比較結果:無論是可讀性還是程序的簡潔性方面,
EL
表達式無疑要勝過許多,唯一的缺點是
EL
表達式必須使用
Servlet2.4
以上的規范。
2. <bean:cookie>
、
<bean:header>
、
<bean:parameter>
標簽和
<bean:write>
標簽處理顯示被
EL
表達式替換
q????????
原形:
<bean:cookie>
、
<bean:header>
、
<bean:parameter>
標簽的作用是,定義一個
JavaBean
類型的變量,從
cookie
、
request header
、
request parameter
中得到該
JavaBean
實例。
<bean:write>
標簽可以通過
JavaBean
變量來做顯示的工作。
q????????
替換方案:利用
EL
表達式來替換。
q????????
示例比較:
<bean:parameter>
標簽的動作:
<bean:parameter id="requestString" name="requestParameterString" />
<bean:write name="requestString"/>
EL
表達式的動作:
${param.requestParameterString}
q????????
比較結果:
EL
表達式默認的
5
個變量:
cookie
、
header
、
headerValues
、
paramValues
、
param
完全可以提供更方便簡潔的操作。
3. <bean:include>
標簽被
<c:import>
標簽替換
q????????
原形:
<bean:include>
標簽的作用是定義一個
String
類型的變量,可以包含一個頁面、一個響應或一個鏈接。
q????????
替換方案:利用
<c:import>
標簽來替換。
q????????
示例比較
<bean:include>
標簽的動作:
<bean:include page="/MyHtml.html" id="thisurlPage" />
<c:import>
標簽的動作:
<c:import url="/MyHtml.html" var="thisurlPage" />
<bean:include>
標簽的
page
屬性所起的作用可以由
<c:import>
標簽來替換,二者的操作結果是一樣的。
q????????
比較結果:這一對標簽的比較沒有明顯區別,而
<bean:include>
標簽有更多屬性提供更多功能,因此替換并不是十分必要。
尤其是當要用到配置在
struts-config.xml
中的
<global-forwards>
元素進行全局轉發頁面時,必須使用
<bean:include>
標簽的
forward
元素來實現。
4. <bean:message>
標簽處理資源配置文件被
<fmt:bundle>
、
<fmt:setBundle>
、
<fmt:message>
標簽合作替換
q????????
原形:
<bean:message>
標簽是專門用來處理資源配置文件顯示的,而它的資源配置文件被配置在
struts-config.xml
的
<message-resources>
元素中。
q????????
替換方案:利用
<fmt:bundle>
、
<fmt:setBundle>
、
<fmt:message>
標簽合作來替換,由
<fmt:bundle>
、
<fmt:setBundle>
設置資源配置文件的實體名稱,再由
<fmt:message>
標簽負責讀取顯示。
q????????
示例
比較
<bean:message>
標簽的動作:
<bean:message key="message.attacksolution"/>
<fmt:bundle>
、
<fmt:message>
標簽的動作:
<fmt:bundle basename="resources.application">
???????? <fmt:message key="message.attacksolution" />
</fmt:bundle>
或
<fmt:setBundle>
、
<fmt:message>
標簽的動作:
<fmt:setBundle basename="resources.application" var="resourceaApplication"/>
<fmt:message key="message.attacksolution" bundle="${resourceaApplication}"/>
q????????
比較結果:這一對標簽對于國際化的支持都相當好,唯一最大的區別在于利用
<bean:message>
標簽所操作的資源配置文件是配置在
struts-config.xml
中的,而
<fmt:message>
標簽所操作的資源配置文件則是根據
<fmt:bundle>
、
<fmt:setBundle>
兩組標簽來得到的。看起來,后者的靈活性不錯,但就筆者的眼光來看,前者更為規范,對于用戶協作的要求也更高。試想,維護一到兩個資源配置文件與維護一大堆資源配置文件哪個更方便呢?自然是前者了,因此除非是不依賴
Struts
框架的應用,否則最好使用
<bean:message>
標簽。
Struts Logic
標簽庫中的標簽在頁面顯示時是時常被用到的,但是常用的卻不一定是最好用的,有了
JSTL
標簽庫和
EL
表達式后,許多
Struts Logic
標簽庫的標簽可以被簡單替換。
1.
所有判斷標簽被
EL
表達式和
<c:if>
標簽替換
q????????
原形:判斷標簽有一個特點,就是需要取得一個實例的變量,因此通過
<bean:define>
標簽來取得實例的變量是必須的,隨后就通過各種判斷標簽來完成判斷的工作。常用的判斷標簽如表
9.30
所示:
表
9.30?
常用判斷標簽
標簽名
|
描述
|
empty
|
判斷變量是否為空
|
notEmpty
|
與
empty
標簽正好相反
|
equal
|
判斷變量是否與指定的相同
|
notEqual
|
與
equal
標簽正好相反
|
lessThan
|
判斷變量是否比指定的小
|
greaterThan
|
判斷變量是否比指定的大
|
lessEqual
|
判斷變量是否小于等于指定的值
|
greaterEqual
|
判斷變量是否大于等于指定的值
|
present
|
檢查
header
、
request parameter
、
cookie
、
JavaBean
或
JavaBean propertie
不存在或等于
null
的時候,判斷成功
|
notPresent
|
與
present
標簽正好相反
|
match
|
比較
String
類型字符串是否與指定的相同
|
notMatch
|
與
match
標簽正好相反
|
q????????
替換方案:利用
EL
表達式和
<c:if>
標簽來替換。
q????????
示例比較:判斷標簽的動作:
<bean:define id="javaBeanName"
name="javaBeanParameter"
property="attack_event_code"
scope="request"/>
<logic:notEmpty name="javaBeanParameter">
???????? javaBeanParameter not empty
</logic:notEmpty>
EL
表達式和
<c:if>
標簽的動作:
<c:if test="${requestScope.javaBeanParameter.attack_event_code != null
&& requestScope.javaBeanParameter.attack_event_code != ''”}>
???????? javaBeanParameter not empty
</c:if>
EL
表達式利用操作符來完成判斷動作,然后通過
<c:if>
標簽來根據判斷結果處理對應工作。
q????????
比較結果:
EL
表達式的操作符對判斷的貢獻很大,
EL
表達式的靈活性是
Struts
判斷標簽無法比擬的,任何判斷標簽都可以通過表達式來實現。
<c:if>
標簽還可以將判斷的結果保存為一個變量,隨時為之后的頁面處理服務。
反觀
Struts
框架的判斷標簽,在工作之前必須先定義被判斷的變量,而判斷后又無法保存判斷結果,這樣的程序設計遠不如
EL
表達式和
<c:if>
標簽的協作來得強大。因此使用
EL
表達式和
<c:if>
標簽來替換判斷標簽是更好的選擇。
2. <logic:iterate>
標簽被
<c:forEach>
標簽和
EL
表達式替換
q????????
原形:
<logic:iterate>
標簽用來對集合對象的迭代,可以依次從該集合中取得所需要的對象。
q????????
替換方案:利用
<c:forEach>
標簽和
EL
表達式的協作替換
<logic:iterate>
標簽。
q????????
示例比較
<logic:iterate>
標簽的動作:
<logic:iterate name="allAttackSolution"
???? id="attackSolution"
???? type="struts.sample.cap1.sample3.entity.AttackSolution">
???????? <bean:write property="attack_event_code" name="attackSolution"/>
???????? <bean:write property="attack_mean" name="attackSolution"/>
???????? <bean:write property="attack_action" name="attackSolution"/>
</logic:iterate>
<c:forEach>
標簽
EL
表達式協作的動作:
<c:forEach items="${requestScope.allAttackSolution}" var="attackSolution">
???????? ${attackSolution.attack_event_code}
???????? ${attackSolution.attack_mean}
???????? ${attackSolution.attack_action}
</c:forEach>
兩個動作都做的是同一件事,從
request
中得到保存的“
allAttackSolution
”參數,該參數為一個集合,集合中的對象為
struts.sample.cap1.sample3.entity.AttackSolution
類型的實例。
<logic:iterate>
標簽本身可以接收集合,保存為一個變量,利用迭代子模式,使
<logic:iterate>
標簽體中的
<bean:write>
標簽將集合中的每個
JavaBean
顯示出來。
提示:在本例中由于要顯示
JavaBean
中的變量屬性,因此
<bean:write>
標簽還需要設置
property
屬性。
替換工作的
<c:forEach>
標簽則相對要方便些,
items
屬性使用
EL
表達式取得集合,然后設置
var
屬性作為集合中對象的變量,最后使用
EL
表達式來顯示數據。
q????????
比較結果:
值得注意的一個地方是,
<logic:iterate>
標簽必須為集合中的對象指定類型,因為標簽庫處理時會將集合中的對象作為
Object
類型得到,然后需要讀取
type
屬性定義的
Java
類為它強制轉型。
而
<c:forEach>
標簽則完全不用,只要符合標準
JavaBean
(為變量屬性提供
get
、
set
方法)的對象都可以通過
EL
表達式來從
var
屬性定義的變量中取得該
JavaBean
的變量屬性。
因此
<c:forEach>
標簽和
EL
表達式的方式更加簡單,也更加靈活。
當然,熟悉
<logic:iterate>
標的程序設計者也可以將
<bean:write>
標簽替換為
EL
表達式而仍然使用
<logic:iterate>
標簽。代碼可以是這樣:
<logic:iterate name="allAttackSolution"
id="attackSolution"
type="struts.sample.cap1.sample3.entity.AttackSolution">
???????? ${attackSolution.attack_event_code}
???????? ${attackSolution.attack_mean}
???????? ${attackSolution.attack_action}
</logic:iterate>
結果一樣,但這種方式比
<bean:write>
標簽顯示方式靈活多了。
3. <logic:redirect>
標簽被
<c:redirect>
和
<c:param>
標簽替換
q????????
原形:
<logic:redirect>
標簽用來轉發到一個頁面,并可以為轉發傳遞參數。
q????????
替換方案:利用
<c:redirect>
和
<c:param>
標簽的協作替換
<logic:redirect>
標簽。
q????????
示例比較:
<logic:iterate>
標簽的動作:
<%
???????? HashMap paramMap = new HashMap();
???????? paramMap.put("userName", "RW");
???????? paramMap.put("passWord", "123456");
%>
?<logic:redirect page="/MyHtml.jsp" name="paramMap" scope="request" />
<c:redirect>
和
<c:param>
標簽協作的動作:
<c:redirect url="/MyHtml.jsp">
???????? <c:param name="userName" value="RW"/>
???????? <c:param name="passWord" value="123456"/>
</c:redirect>
兩個動作都做的是同一件事,都將轉發到當前
Web Context
下的“
MyHtml.jsp
”去,而且都將為它提供兩個參數。最后的轉發鏈接看起來應該如下所示:
http://localhost:8080/test/ MyHtml.jsp? userName=RW&password=123456
q????????
比較結果
一眼就可以看出,
<logic:redirect>
標簽的可讀性不強,它的
name
屬性表示的是一個
Map
類型的變量。如果還有
property
屬性,則
name
屬性指的是一個標準
JavaBean
。
property
屬性指的是
JavaBean
中的一個
Map
類型的變量屬性,通過
Map
的“名值對”來為轉發頁面傳遞參數。如果轉發參數是來自于一個
Map
或
JavaBean
中的
Map
類型變量屬性,那還好,因為可以在
Java
類中處理。可是如果純粹是從頁面上取得某些值作為轉發參數,那就困難了,必須像本示例所給出的那樣,自行定義一個
Map
實例。這種情況下,頁面就會看到
Java
語言的片段,既麻煩又不符合標準。
而使用
<c:redirect>
和
<c:param>
標簽協作,由于包含在
<c:redirect>
標簽體內的
<c:param>
標簽可以有多個,因此顯式地提供
<c:param>
標簽就完成了給出轉發參數的工作,即使用到
JavaBean
,也可以使用
EL
表達式來實現。
綜上所述,利用
<c:redirect>
和
<c:param>
標簽來代替
<logic:redirect>
標簽是有必要的。
Struts
框架和
JSTL
并不是互相沖突的兩種技術,雖然
Struts
框架提供了功能不錯的標簽庫,但是使用
JSTL
可以簡化
Struts
框架標簽庫復雜的地方,這對于服務器端表示層框架的
Struts
來說幫助很大。
Struts
的
HTML
標簽庫無法使用
JSTL
來替換,但是,使用
EL
表達式作為一些
value
屬性,來做賦值的工作仍然不失為一種好的選擇。因此,在
JSTL
已經比較成熟的今天,使用
Struts
框架和
JSTL
整合來作
JSP
頁面將使程序設計更為輕松。