Struts 2核心技術與Java EE框架整合開發實戰

17.3 Struts 2整合JSF
目前基於JSF規範較成熟的框架有兩個,一個Sun的JSFUI,另一個是Apache的MyFaces框 架。因爲Struts 2提供了對MyFaces更好的插件支持,因此本示例採用Apache的MyFaces。整合之前讓我們先來比較一下這兩種表示層的框架。
17.3.1 Struts 2整合JSF的優點
下面從不同方面比較一下Sturts 2與JSF各自的特點。
首先,在標籤庫方面,Struts 2的標籤庫相對要少一些,且不可以自定義;而JSF可以自定義標籤。JSF框架擁有豐富的頁面組件,如果需要的話可以自己編寫相應的組件,或者 擴展組件;而在JSP的頁面中JSF提供了頁面驗證標籤,可以做簡單的長度和類型的驗證。Struts 2的驗證可以有兩種方式,form驗證與validator驗證,功能上要比JSF強大。JSF的組件都是綁定到Bean的,而且數據驗證的方法也可以綁 定,這一點可以增強驗證的功能。而對於驗證的錯誤提示信息,它們都提供了國際化,使得驗證更人性化。
其次比較一下導航,二者之間的導航功能的相同點是都通過在XML文件中配置導航規則。Struts 2的XML中配置頁面跳轉的類型,如轉發,重 定向,由Action返回的字符串來決定導航的目標;而JSF在導航規則中設定頁面導航,當某個頁面請求到來時,根據導航規則調用指定的Action方法進行處理,並返回一個邏輯視圖,然後跳轉到與邏輯視圖對應的頁面。JSF同時支持在頁面中綁定按鈕觸發Action的具體方法,導航原理也是一樣的。
最後比較一下Struts 2與JSF處理請求的方式。Struts 2調用指定的方法處理請求(如果沒有指定具體方法則默認調用excute方法)。JSF採用了普通的POJO類作爲它的Action,將Action類綁定到頁面組件,通過值變監聽與事件監聽進行請求處理。相比之下,JSF處理請求的方式要比Struts2複雜,不方便系統升級。
總而言之,如果將JSF做爲Struts 2的視圖層,用Struts 2的Action做模型,可以開發出完美的應用系統。
接下來就將講解Struts 2與JSF 的結合使用。
17.3.2 Struts 2與JSF整合過程
每種框架都有它獨到的設計之處,Struts 2的可擴展性使得它的生命力非常頑強。Struts 2提供了多種框架的插件包,它與MyFaces 整合就是利用插件來實現的。下面我們介紹如何進行二者的結合應用。
首先下載Struts 2的JSF插件,下載地址是 http://struts.apache.org/ downloads.html。目前最高的插件版本是2.0.11,我們使用這個最新的版本與myfaces進行整合。
Apache的MyFaces下載地址是http://myfaces.apache.org. /download.html,目前的最高版本是1.2.2,本示例使用的是1.1.5。下載後得到名爲myfaces-core-1.1.5的壓縮文 件,將該文件解壓,得到lib包下的運行庫文件(.jar文件)。
17.3.3 整合應用實例
2008年是奧運年,因此我們採用目前最流行的奧運啦啦隊員的選拔活動爲主題,設計一個Struts 2+JSF應用的示例。
奧運啦啦隊員選拔隊員的設計分爲3種功能:增加選手、查詢選手、修改選手。
按如下的順序創建示例程序。
(1)配置環境:配置Struts 2+JSF整合過程的運行環境。
(2)配置struts.xml文件:配置JSF攔截器與請求的Action。
(3)創建頁面:註冊選手頁面,顯示所有選手列表頁面,修改選手頁面。
(4)創建JavaBean。選手信息類PlayerInfo與控制器類OlympicAction。
(5)配置Web應用文件:配置Struts 2請求轉發控制器。
(6)發佈運行:演示發佈運行後的結果頁面。
下面詳細介紹各個環節的實現過程。
(1)配置應用程序運行環境。
添加Struts 2核心資源包、Struts 2的JSF 插件包、MyFaces資源包。
(2)配置struts.xml。
利用Struts 2+JSF開發視圖層,需要的配置文 件是struts.xml。這個文件配置信息分爲兩個部分,一個是JSF 攔截器的配置,另一個是Struts 2的Action配置。
首先看一下JSF攔截器的配置:
在struts.xml文件中需要配置JSF的攔截器,使得所有的JSF的請求都能被正確處理。這個攔截器在Struts的插件包中已經定義好了,繼承這個包就可以使 用這些攔截器。攔截器的配置如代碼17-17所示。
代碼17-17 struts.xml中JSF攔截器的配置
<!-- 重寫攔截器,將其命名在包myJSF中 -->
<package name="myJSF" extends="JSF-default">
<interceptors>
<interceptor-stack name="JSFFullStack">
<interceptor-ref name="params" />
<interceptor-ref name="basicStack" />
<interceptor-ref name="JSFStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="JSFFullStack" />
</package>
接下來配置請 求的Action。
在請求的Action配置中需要繼承myJSF攔截器,用來處理JSF頁面的組件。
本應用中來自頁面的請求共有4種,如下所示。
l welcome.action:請求顯示歡迎頁面。配置的 result類型爲JSF,使用JSF解析welcome.jsp頁面中的組件。
l view.action:請求顯示所有選手列表。配置的result類型爲JSF,使用JSF解析view.jsp頁面中的組件,顯示所有已報名選手的列 表信息。
l register.action:請求增加選手。配置兩種result。
result的name=“JSF”,使用JSF解析register.jsp頁面中的組件。
result 的name=“view”、type類型爲chain,即增加選手後直接請求顯示所有選手類表的view.action。
l findone.aciton:請求顯示修改選手信息
result類型爲“JSF”,使用JSF解析 findone.jsp頁面中的組件。
result 的name=“view”、type類型爲“chain”,即修改選手後直接請求顯示所有選手類表的view.action。
struts.xml的詳細配置,如代碼17-18所示。
代碼17-18 struts.xml的詳細配置
<!--定義我們的Action的包,並且要繼承myJSF -->
<package name="olympic" extends="myJSF">
<!--首次進入的歡迎頁面-->
<action name="welcome">
<result type="JSF"/>
</action>
<!--顯示所有選手-->
<action name="view" class="com.sunyang.olympic.OlympicAction">
<result name="success" type="JSF" />
</action>
<!--選手報名-->
<action name="register" class="com.sunyang.olympic.OlympicAction">
<result name="success" type="JSF" />
<result name="view" type="redirect">view.action</result>
</action>
<!--修改信息-->
<action name="findone" class="com.sunyang.olympic.OlympicAction" method="findone">
<result name="success" type="JSF" />
<result name="view" type="redirect">view.action</result>
</action>
</package>
(3)創建頁面。
示例程序共設計4個頁面,分述如下。
l welcome.jsp。歡迎頁面。該頁面顯示兩種鏈接:“我現在就要報名”鏈接到新增加選手頁面;“我想看看都誰報名了”鏈接到查看所有選手列表信息頁 面。
l register.jsp。增加用戶頁面。單擊歡迎頁面的“我現在就要報名”,跳轉到本頁面。該頁面顯示增加選手的信息,頁面使用JSF輸入組件標籤,表 單提供用戶編號、姓名、年齡、性別與聯絡方式,所有的信息都由選手填寫,而且實現數據驗證功能,對於不合法的數據類型同時提供錯誤信息。
l view.jsp。查看所有選手列表信息。該頁面採用JSF製表標籤,循環遍歷顯示所有選手信息。該頁面還提供了另一個鏈接“這裏還沒有我”,單擊該鏈接 跳轉到增加選手頁面。
l findone..jsp。該頁面用來提供選手修改個人信息。
單擊查 看所有選手列表信息頁面的選手編號,跳轉到本頁面。本頁面採用JSF輸入類標籤,顯示該選手編號對應的詳細信息,如選手編號、選手姓名、性 別、年齡及聯繫方式,供選手修改。修改後單擊“我確定修改”,跳轉到顯示所有選手類表頁面。
①歡迎頁面welcome.jsp,如代碼17-19所示。
代碼17-19 welcome.jsp
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%@ taglib prefix="f" uri="http://java.sun.com/JSF/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/JSF/html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'welcome.jsp' starting page</title>
</head>
<body>
<f:view>
<h3>奧運啦啦隊員海選報名啦!!!</h3>
<h3>請選擇:</h3>
<h:outputLink value="register.action">
<h:outputText value="我現在就要報名!!!" />
</h:outputLink>
<h:outputLink value="view.action">
<h:outputText value="我想看看都誰報名了!!!" />
</h:outputLink>
</f:view>
</body>
</html>
②增加選手的註冊頁面register.jsp。
單擊“我現在就要報名”跳轉到註冊頁面,這個頁面的代碼很多,標籤中的“value”值就是配置在Action中注入的 player的各種屬性值,如代碼17-20所示。
代碼17-20 register.jsp
<%@ page language="java" contentType="text/html; charset=GBK"%>
<%@ taglib prefix="f" uri="http://java.sun.com/JSF/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/JSF/html" %>
<html>
<head>
<title>註冊信息</title>
</head>
<body>
<f:view>
<h3>同一個世界,同一個夢想</h3>
<h3>感謝您對奧運的支持,請填寫下列信息......</h3>
<h:form>
<h:panelGrid columns="3">
<h:outputText value="選手編號" />
<h:inputText id="id" size="30" value="#{action.player.id}" required="true" />
<h:message for="id" />
<h:outputText value="選手姓名" />
<h:inputText id="name" size="30" value="#{action.player.name}"
required="true">
<f:validateLength minimum="2" maximum="100" />
</h:inputText>
<h:message for="name" />
<h:outputText value="選手性別" />
<h:inputText id="sex" size="30" value="#{action.player.sex}" required="true">
<f:validateLength minimum="1" maximum="6" />
</h:inputText>
<h:message for="sex" />
<h:outputText value="選手年齡" />
<h:inputText id="age" size="30" value="#{action.player.age}" required="true">
<f:validateLength minimum="1" maximum="100" />
</h:inputText>
<h:message for="age" />
<h:outputText value="聯繫方式" />
<h:inputText id="tel" size="30" value="#{action.player.tel}" required="true">
<f:validateLength minimum="2" maximum="13" />
</h:inputText>
<h:message for="tel" />
</h:panelGrid>
<h:commandButton value="報名了" action="#{action.save}" />
<br/>
</h:form>
</f:view>
</body>
</html>
③查看所有選手信息列表頁面view.jsp。
在註冊選手頁面,填寫選手信息後,單擊“報名了”跳轉到查看所有選手的頁面,該頁面將所有選手信息遍歷顯示到 表格中。標籤<h:dataTable>用來顯示錶格,<f:facet>用來顯示錶頭,<h:column>用來 顯示錶列,詳細的設計如代碼17-21所示。
代碼17-21 view.jsp
<%@ page language="java" contentType="text/html; charset=GBK"%>
<%@ taglib prefix="f" uri="http://java.sun.com/JSF/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/JSF/html" %>
<html>
<head>
<title>查看所有選手報名信息</title>
</head>
<body>
<f:view>
<h3>已經有這麼多人報名了!!!!!</h3>
<h3>這裏有我嗎?沒有請單擊加入他們</h3>
<h:dataTable value="#{action.select}" var="p" style="text-align:center;width:500px"
border="1">
<h:column>
<f:facet name="header">
<h:outputText value="選手編號" />
</f:facet>
<h:outputLink value="findone.action">
<f:param name="playerid" value="#{p.id}" />
<h:outputText value="#{p.id}" />
</h:outputLink>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="選手姓名" />
</f:facet>
<h:outputText value="#{p.name}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="選手性別" />
</f:facet>
<h:outputText value="#{p.sex}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="選手年齡" />
</f:facet>
<h:outputText value="#{p.age}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="聯繫方式" />
</f:facet>
<h:outputText value="#{p.tel}" />
</h:column>
</h:dataTable>
<p>
<h:outputLink value="register.action">
<h:outputText value="這裏還沒有我!!" />
</h:outputLink>
</p>
</f:view>
</body>
</html>
④修改選手頁面findone.jsp。
單擊查看顯示所有選手列表頁面中的選手編號,跳轉到修改選手信息頁 面。
在findone.jsp中需要增加一個<h:inputHidden>標籤用來隱式的標識“playerid”。將選手編號爲“playerid”的屬性值顯示到頁面中。最後提交action的modify方法,詳細的設計如 代碼17-22所示。
代碼17-22 findone.jsp
<%@ page language="java" contentType="text/html; charset=GBK"%>
<%@ taglib prefix="f" uri="http://java.sun.com/JSF/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/JSF/html" %>
<html>
<head>
<title>修改信息</title>
</head>
<body>
<f:view>
<h3>同一個世界,同一個夢想</h3>
<h3>感謝您對奧運的支持,請修改下列信息>>></h3>
<h:form>
<h:inputHidden value="#{action.playerid}"/>
<h:panelGrid columns="3">
<h:outputText value="選手編號" />
<h:inputText id="id" size="30" value="#{action.player.id}" required="true" />
<h:message for="id" />
<h:outputText value="選手姓名" />
<h:inputText id="name" size="30" value="#{action.player.name}" required="true">
<f:validateLength minimum="2" maximum="100" />
</h:inputText>
<h:message for="name" />
<h:outputText value="選手性別" />
<h:inputText id="sex" size="30" value="#{action.player.sex}" required="true">
<f:validateLength minimum="2" maximum="6" />
</h:inputText>
<h:message for="sex" />
<h:outputText value="選手年齡" />
<h:inputText id="age" size="30" value="#{action.player.age}"
required="true">
<f:validateLength minimum="1" maximum="100" />
</h:inputText>
<h:message for="age" />
<h:outputText value="聯繫方式" />
<h:inputText id="tel" size="30" value="#{action.player.tel}" required="true">
<f:validateLength minimum="2" maximum="13" />
</h:inputText>
<h:message for="tel" />
</h:panelGrid>
<h3>感謝您對奧運的支持,提交前請確認您的信息</h3>
<h:commandButton value="我確定修改!!!" id="modifyCommand" action=
"#{action.modify}" />
<br/>
</h:form>
</f:view>
</body>
</html>
(4)創建JavaBean,包括選手信息類PlayerInfo與控制器類OlympicAction。
①首先創建選手信息類PlayerInfo.java。
該類中定義選手的各種屬性,包括id(編號)、name(名稱)、age(年齡)、sex(性別)、 tel(聯繫方式),如代碼17-23所示。
代碼17-23 PlayerInfo.java
package com.sunyang.olympic;
public class PlayerInfo {
private int id;
private String name;
private int age;
private String sex;
private int tel;

//定義默認的構造函數
public PlayerInfo(){}
//重寫構造函數
public PlayerInfo(int id,String name,String sex,int age,int tel){
this.id=id;
this.name=name;
this.sex=sex;
this.age=age;
this.tel=tel;

}
//省略屬性的getter和setter方法
}
②然後創建控制器類OlympicAction.java。
OlympicAction.java用 來處理增加選手頁面、修改選手頁面、 顯示所有選手列表頁面、修改選手信息頁面功能的各種請求。該類需要繼承ActionSupport,注入選手信息類PlayerInfo對象。添 加處理頁面表單的方法如下。
l 預存儲數據:本應用沒有持久化,因此 這裏採用預先填寫3條數據,存儲在List對象中。
l 增加選手:單擊增加選手頁面中的“報名了”,觸發該方法。本應用中的所有選手信息,除了預先存儲的數據以外,新增加的選手數據,均存儲在會話 session當中。
l 查詢所有選手:單擊“我想看看都誰報名了”或是在修改選手頁面中更新選手信息後,單擊“我確定修改!”時觸發此方法,此方法調用存儲在List中的數據, 如果會話已經被創建,就返回會話當中的列表List;否則返回預先存儲的列表List。
l 查找單個選手:該方法根據頁面表單中選手的編號,遍歷會話session中存儲的選手對象,並將該對象中的數據返回給修改選手信息頁面,供選手修改。
l 修改選手信息:當選手在修改信息頁面中更新當前信息後, 點擊“我確定修改!”提交到本方法。 此方法將根據提交的選手編號,更新存 儲在會話session中的對象信息,並跳轉到查看所有選手信息列表頁面。
具體如代碼17-24所示。
代碼17-24 OlympicAction.java
package com.sunyang.olympic;
public class OlympicAction extends ActionSupport {
// 注入選手POJO
private PlayerInfo player;
// 實例化選手對象
public OlympicAction(){
player=new PlayerInfo(); }
// 定義選手的id屬性,用來接受頁面值信息
private int playerid;
// 省略他們的getter和setter
//定義封裝選手對象的list
List <PlayerInfo> list=new ArrayList<PlayerInfo>();
// 硬性地存儲3條數據,
public List<PlayerInfo> setValue(){
// 定義會話session
HttpSession session = ServletActionContext.getRequest().getSession();
if(session.getAttribute("player")==null){
PlayerInfo no1=new PlayerInfo(2008,"Andy","man",25,1111111111);
PlayerInfo no2=new PlayerInfo(2009,"Lily","female",25,22222222);
PlayerInfo no3=new PlayerInfo(2010,"Kaka","man",25,1111111111);
list.add(no1);
list.add(no2);
list.add(no3);
session.setAttribute("player", list);
}
list=(List)session.getAttribute("player");
return list;
}
// 遍歷所有的選手並將其傳值到頁面
public List<PlayerInfo> getSelect(){
List listValue=new ArrayList();
listValue=setValue();
return list;
}
// 增加選手對象
public String save(){
HttpSession session = ServletActionContext.getRequest().getSession();
if(session!=null){
list=setValue();
list.add(player);
session.setAttribute("player",list);
}
return "view";
}
// 遍歷單個選手,並將其值傳到頁面
public String findone(){
HttpSession session = ServletActionContext.getRequest().getSession();
if(session!=null){
list=(List)session.getAttribute("player");
for(int i=0;i<list.size();i++){
PlayerInfo p=list.get(i);
if(p.getId()==playerid){
this.player=p;
}
}
}
return "success";
}
// 用來修改選手信息
public String modify(){
HttpSession session = ServletActionContext.getRequest().getSession();
if(session!=null){
list=(List)session.getAttribute("player");
for(int i=0;i<list.size();i++){
PlayerInfo p=list.get(i);
if(p.getId()==player.getId()){
list.remove(p);
list.add(player);
}
}
}
return "view";
}
}
(5)配置Web應用文件。
Struts 2中使用的JSF的UI標籤、JavaBean的綁定功能,都需要使用JSF的Servlet來處理。因此在Web應用文件中需要配置的JSFSevlet處理.action的所有請求,同時要配置 Struts 2的請求轉發。如代碼17-25所示。
代碼17-25 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="JSF" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.Struts 2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<!--JSF的servlet -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置所有的.action的請求都有JSF的servlet處理 -->
<servlet-mapping>
<servlet-name>faces</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
(6)最後發佈運行。

單擊“我想看看都誰報名了”後,進入到查看所有隊員信息的頁面。


單擊歡迎頁面的“我現在就要表名!!!”或者頁面的“這裏還沒有我”後進入到增加新的啦啦隊 選手的頁面。
當單擊“報名了”後,跳轉到查看所有選手信息的頁面。


單擊選手編號,可以修改選手的各項信息。
單擊“我確定修改!!!”按鈕後跳轉到查看所有選手頁面。


至此,Struts 2和 JSF的整合就完成了,發佈運行後,得到預期的效果。但要注意的是,示例中我們將值存儲在session會話中,並 沒有被真正的持久化,如果要想將數據持久化,就需要增 加持久層及業務層相應代碼,這些操作可以參考其它整合持久化框架的章節。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章