Struts是一個用來構(gòu)建企業(yè)級J2EE應(yīng)用程序的流行框架。通過使用Struts,J2EE Web應(yīng)用程序的開發(fā)變得更加輕松且更易于管理。Beehive是Apache軟件基金會(Apache Software Foundation,ASF)的一個開源項目,它在Struts的基礎(chǔ)上構(gòu)建一個簡單的頁面流(Page Flow)模型,使Web應(yīng)用程序的開發(fā)變得更加簡單明了。通過使用新的 JSR-175和 JSR-181元數(shù)據(jù)注釋功能,Beehive減少了J2EE應(yīng)用程序開發(fā)的編碼量。本文將介紹Beehive頁面流技術(shù),以及如何使用它來提高Struts軟件的生產(chǎn)力和質(zhì)量。本文還介紹了如何將該技術(shù)遷移到普通的Struts應(yīng)用程序中去。本文假定讀者對Struts有一定的了解。
Struts是Apache軟件基金會的Jakarta項目的一部分,是一個基于模型-視圖-控制器(Model-View-Controller,MVC)設(shè)計模式構(gòu)建Web應(yīng)用程序的開源框架。Struts使用動作(action)類來構(gòu)建框架的控制器組件。典型的Struts應(yīng)用程序要求使用大量動作類來處理某一流程流(process flow)的幾個動作,并要求使用一個XML配置文件來聲明跳轉(zhuǎn)。因此,像數(shù)據(jù)管理和維護(hù)之類的問題已經(jīng)成為現(xiàn)有Struts應(yīng)用程序的一個主要關(guān)注點。圖1顯示了一個示例應(yīng)用程序的流程圖,意在對Struts和頁面流作一比較。
圖1. LoginProcessFlow應(yīng)用程序的流程圖
在Struts中開發(fā)該應(yīng)用程序的時候,通常要求針對每個動作(begin、signup、login和logout)使用動作類。清單1顯示了一個典型的動作類,在本例中是圖中描繪的begin動作。
清單1. BeginAction.java
為完成該Struts應(yīng)用程序,需要針對signup、login和logout使用類似的動作類。HTTP會話被用來跨動作傳遞數(shù)據(jù),這使得管理和維護(hù)數(shù)據(jù)變得很困難。Struts使用一個配置文件(struts-config.xml)來聲明到JSP頁面的跳轉(zhuǎn)。因此,要更改業(yè)務(wù)規(guī)則通常還需要更改動作類和配置文件。具體來說,更改業(yè)務(wù)邏輯要求更改動作,而更改業(yè)務(wù)流程則要求更改配置文件(通常二者都需要更改)。清單2顯示的是LoginProcessFlow應(yīng)用程序的典型的struts-config.xml文件。
頁面流
了解開發(fā)Struts應(yīng)用程序的復(fù)雜性后,讓我們來深入研究頁面流及其提供的功能。Beehive的NetUI頁面流框架使得開發(fā)和維護(hù)工作更加輕松和易于管理。清單3是一個頁面流控制器(通常簡寫為Java頁面流(Java Page Flow,JPF)),用于前面所述的LoginProcessFlow應(yīng)用程序。頁面流框架使用一個JPF文件來代替所有的動作類和配置文件。
清單3. LoginPageFlowController.java
package comparison.pageflows;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.annotations.Jpf;
public class LoginPageFlowController extends PageFlowController
{
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/index.jsp")
}
)
protected Forward begin()
{
//initial processing
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/mypage.jsp"),
@Jpf.Forward(name = "failure", path = "/index.jsp")
}
)
protected Forward login()
{
//authentication logic
if(user_authenticated)
return new Forward("success");
else
return new Forward("failure")
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/confirm.jsp")
}
)
protected Forward signup()
{
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/begin.do")
}
)
protected Forward logOut()
{
return new Forward("success");
}
}
清單3清楚地顯示了Struts和頁面流所要求的開發(fā)工作量和維護(hù)任務(wù)方面的差別。
如前所述,JPF是注釋驅(qū)動的,并且不需要在配置文件中聲明跳轉(zhuǎn)。頁面流利用Beehive框架自動生成基于注釋的配置文件(如:JPF中聲明的 @Jpf.Action)。清單3顯示了如何在一個JPF文件中處理所有的動作以及以注釋的形式聲明的跳轉(zhuǎn)。例如begin動作,如果成功的話,就跳轉(zhuǎn)到index.jsp頁面。您可以清楚地看到動作如何處理邏輯,以及簡單的注釋如何處理跳轉(zhuǎn),這些注釋在使用前要進(jìn)行聲明。注釋可以在動作級別聲明,也可以在頁面流級別聲明。在Beehive提供的眾多注釋中,@Jpf.Forward、@Jpf.Controller和 @Jpf.Action是最常用的。對所有注釋的討論不在本文的范圍之內(nèi)。要獲得有關(guān)的更多信息,請訪問 Page Flow Annotations說明文檔。
Struts應(yīng)用程序要求對每個動作使用一個動作類,而使用頁面流開發(fā)應(yīng)用程序只需一個JPF文件就足夠了。頁面流提供一種簡單的、單一文件的編程模型。在構(gòu)建復(fù)雜的Web應(yīng)用程序時,頁面流使開發(fā)人員能夠立即開始開發(fā),而無需經(jīng)歷與學(xué)習(xí)Struts有關(guān)的曲折過程。當(dāng)遇到因為業(yè)務(wù)規(guī)則的更改而引起的修改時,頁面流顯得更為靈活。
會話數(shù)據(jù)管理
頁面流提供的編程框架添加了多處改進(jìn),剛剛就是注釋驅(qū)動的自動生成和同步XML配置文件的例子。接下來讓我們看看頁面流的另一個優(yōu)點:使用頁面流級的(Page Flow-scoped)變量可以更輕松地跨多個動作管理和維護(hù)數(shù)據(jù)。
在Struts中,動作類獲取會話對象并設(shè)置屬性(或賦值),其后其他動作會對這些屬性進(jìn)行檢索。清單4顯示的例子有兩個Struts動作類:LoginAction和LogOutAction,它們使用HTTP會話共享一個變量username。
清單4. LoginAction.java和LogOutAction.java
package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class LoginAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
String username = form.getUserName();
// get this session
HttpSession session = request.getSession();
session.setAttribute("username", username);
return (mapping.findForward("login"));
}
}
package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class LogOutAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// get this session
HttpSession session = request.getSession();
String username = session.getAttribute("username");
//logic to log out user
return (mapping.findForward("logout"));
}
}
上面的代碼清楚地顯示了LoginAction和LogOutAction顯式地共享username變量的方式,在本例中是使用HTTP會話。跨多個動作類管理這些會話變量可能會相當(dāng)復(fù)雜。
相比之下,頁面流提供一個以JPF級別聲明的簡單變量,它可以跨所有的頁面流動作共享數(shù)據(jù)。在清單5的例子中,以頁面流級別聲明了一個String變量(username),并在login和logout動作中使用它。
清單5. LoginPageFlowController.java
package comparision.pageflows;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.annotations.Jpf;
public class LoginPageFlowController extends PageFlowController
{
private String username;
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/index.jsp")
}
)
protected Forward begin()
{
//intial processing
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/mypage.jsp"),
@Jpf.Forward(name = "failure", path = "/index.jsp")
}
)
protected Forward login(ProfileForm form)
{
username = form.getUsername();
//authentication logic
if(user_authenticated)
return new Forward("success");
else return new Forward("failure")
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/confirm.jsp")
}
)
protected Forward signup()
{
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/begin.do")
}
)
protected Forward logOut(ProfileForm form)
{
logout(username);
return new Forward("success");
}
}
如您所見,共享變量的處理方式簡單直觀。
頁面流的優(yōu)點
除了易于使用和其他優(yōu)點之外,這里還列出了頁面流的一些重要優(yōu)點:
- 頁面流是模塊化的:簡單的Web應(yīng)用程序可能包括幾個頁面流,每個頁面流處理一部分邏輯相關(guān)的功能。可以使用共享頁面流或全局頁面流來處理異常和未處理的動作。
- 頁面流是可嵌套的:頁面流嵌套提供細(xì)粒度、可重用的模塊化,以處理功能和數(shù)據(jù)。
- 頁面流是有狀態(tài)的:由于可以聲明頁面流級的變量,所以數(shù)據(jù)管理非常容易。
- 頁面流是注釋驅(qū)動的:最后但并非最不重要的是,注釋驅(qū)動的編程是一個受歡迎的改變,它替代了多文件的、分散代碼的編程。
盡管本文僅僅是介紹一些基本知識,但是我希望讀者能對這些特性如何有助于創(chuàng)建可管理的大型應(yīng)用程序有所了解。模塊化和嵌套特性對于開發(fā)可重用的Web應(yīng)用程序組件特別有用,而諸如會話數(shù)據(jù)管理和豐富的數(shù)據(jù)綁定標(biāo)簽之類的特性則有助于簡化編程。
集成Struts和頁面流
我希望前面的一節(jié)能夠說明頁面流是Struts的一個有用擴(kuò)展。現(xiàn)在,讓我們來看一看用什么方法能夠把頁面流的功能添加到現(xiàn)有Struts項目中,或者如何使Struts和頁面流應(yīng)用程序能夠互操作。
頁面流構(gòu)建于Apache Struts 1.1的基礎(chǔ)之上。每個頁面流都被編譯為一個Struts模塊。因此,頁面流和Struts 1.1應(yīng)用程序可以緊密合作。
有兩種在頁面流應(yīng)用程序中使用Struts組件的方法:
- 利用Struts互操作性:在頁面流應(yīng)用程序中直接使用Struts組件。
- 利用Struts合并功能:將簡單的Struts應(yīng)用程序?qū)氲叫碌捻撁媪鲬?yīng)用程序中。
集成:Struts互操作性
Struts互操作性功能允許用戶使用現(xiàn)有的基于Struts的組件但不修改它們。這允許現(xiàn)有的Struts組件與頁面流組件交互,并可以充分利用現(xiàn)有的JSP頁面、動作類、表單bean和流配置。
Struts和頁面流應(yīng)用程序可以在一個Web應(yīng)用程序中共存并交互。要從頁面流跳轉(zhuǎn)到(純)Struts模塊,只需引用Struts模塊中對應(yīng)的動作即可。反之亦然:在Struts模塊中,只需配置一個指向頁面流中對應(yīng)方法的動作,就可以跳轉(zhuǎn)到頁面流。
頁面流可以用作前端控制器處理初始請求,然后把控制權(quán)轉(zhuǎn)交給Struts中的動作,這些動作進(jìn)行某種處理之后會將控制權(quán)再傳回給頁面流。在嵌套頁面流中可以看到類似的模式。也可以用這種方式在Struts應(yīng)用程序和頁面流之間共享表單bean。
- 在頁面流中,可以導(dǎo)入并使用由Struts 1.1模塊定義的表單bean。因此用戶可以快速地利用現(xiàn)有的Struts代碼,而不必修改Struts動作類和表單bean。
- 在Struts 1.1動作類中,可以導(dǎo)入和使用頁面流定義的表單bean。
包含Struts模塊和頁面流(如果它們要進(jìn)行互操作的話)的文件必須在同一個Web項目中。
對Struts模塊和頁面流的要求
這里是對將在同一個Web項目中進(jìn)行互操作的Struts模塊和頁面流的要求:
- 現(xiàn)有的Struts應(yīng)該是版本1.1。
- 包含Struts模塊的文件必須和頁面流屬于同一個Web項目。
- JAR文件中所有已編譯的、與Struts相關(guān)的類(字節(jié)碼)必須存放在WEB-INF/lib目錄下。
- 為避免命名沖突,Struts模塊名稱和頁面流目錄名稱在整個Web項目中必須是惟一的。
- 同一個頁面流目錄下的JSP頁面總是在頁面流JPF上下文中呈現(xiàn)。調(diào)用動作和呈現(xiàn)在Struts模塊上下文中的JSP頁面應(yīng)當(dāng)存放到其他目錄下,比如/strutsModule目錄下。
- 如果頁面流控制器類和Struts動作類共享同一表單bean,可以將表單bean定義為內(nèi)部類,也可以將其定義為外部Java文件。將表單bean類導(dǎo)入頁面流JPF和將要使用它的Struts動作類中。
- 在共享表單bean時,Struts模塊的XML文件中 屬性值必須與頁面流的jpf-struts-config-.xml文件中生成的name屬性值精確匹配。
- 默認(rèn)情況下,頁面流的范圍是從表單bean實例到請求。共享數(shù)據(jù)的最容易的方式就是在Struts和頁面流之間傳遞會話級(session-scoped)表單bean。這可以通過使用Struts合并功能將(為頁面流生成的)Struts配置XML合并到會話級表單bean中來實現(xiàn)。
- 如果一個Web項目綜合使用了頁面流和Struts模塊,必須通過編輯項目的 /WEB-INF/web.xml文件注冊每個Struts模塊。
互操作性的一個例子
本例顯示了頁面流動作如何調(diào)用Struts動作以及Struts動作如何調(diào)用頁面流動作。本例的目的僅僅是顯示互操作性,因此代碼并不完整。PageFlowActionA將控制權(quán)和會話級表單bean傳遞給StrutsActionA。StrutsActionA進(jìn)行某種處理并跳轉(zhuǎn)到Page.jsp,后者包含一個Struts動作StrutsActionB。StrutsActionB將控制權(quán)傳回PageFlowActionB。
集成:Struts合并
Struts合并可以將簡單的Struts應(yīng)用程序轉(zhuǎn)換為頁面流應(yīng)用程序。這種方法是將簡單的Struts應(yīng)用程序遷移到頁面流框架中的一種快捷方式。
Struts合并功能不同于Struts互操作功能。對于Struts合并功能,可以在控制器級別指定Struts XML配置文件,如下:
/**
* @jpf:controller struts-merge="/WEB-INF/struts-config-merge-example.xml"
*/
public class ExampleStrutsMergeController extends PageFlowController
{
...
}
這一步允許現(xiàn)有的純Struts的XML配置文件與頁面流的生成的jpf-struts-config-.xml文件合并(在項目編譯時)。Struts合并功能的目的是重寫頁面流的默認(rèn)值,或者為頁面流指定頁面流注釋或其屬性不提供的設(shè)置。Struts合并文件通常很小,而且僅僅修改頁面流及其動作和表單bean。盡管可以將動作映射添加到Struts合并文件中,但并不推薦這種實踐。Struts動作映射應(yīng)當(dāng)使用 @jpf注釋在頁面流的 .jpf文件中聲明,然后(如果需要的話)使用Struts合并文件修改。
還可以使用struts合并功能,將純Struts應(yīng)用程序中的配置數(shù)據(jù)讀入頁面流應(yīng)用程序的配置文件。一般來說,頁面流的配置文件完全由應(yīng)用程序的Java源文件(具體地說是由散布于控制器文件的元數(shù)據(jù)注釋)生成。但是,如果要把Struts模塊集成到應(yīng)用程序中,配置文件既可以由Java源文件生成,也可以由Struts模塊的配置文件生成。如果是后者,就可以在生成的配置文件中靈活地更改或添加任何標(biāo)簽。例如,可以將一個動作表單的默認(rèn)作用域從請求級重寫為會話級。為此,只需創(chuàng)建重寫頁面流配置文件的Struts配置文件的適當(dāng)部分,然后從頁面流的Java源文件中引用這個重寫后的文件,正如上面的例子所示。
考慮到諸如現(xiàn)有Struts的復(fù)雜性、時間和工作量等因素,可以在頁面流中綜合使用Struts合并功能和Struts互操作性功能,以取得最佳效果。
Beehive
Apache Beehive Project(蜂巢計劃)不只包含頁面流。它有三個子項目:
- NetUI PageFlows(NetUI頁面流):本文中介紹的Web應(yīng)用程序框架。
- Controls:一個幫助程序員構(gòu)建組件(如:JavaBean,它將元數(shù)據(jù)合并到它的編程模型中)的輕量級組件框架。該項目也提供一些預(yù)制的控件。
- Web Services:一個針對JSR-181規(guī)范所描述的Web services的注釋驅(qū)動的編程模型。
Java頁面流全面支持控件,控件是訪問企業(yè)資源和包裝業(yè)務(wù)邏輯的簡單的編程模型抽象。Java控件使開發(fā)人員可以在一個簡單直觀的環(huán)境中使用方法、事件和屬性訪問J2EE平臺的強大功能(安全性、事務(wù)和異步),而且能夠構(gòu)建遵從面向服務(wù)架構(gòu)(SOA)的最佳實踐的Web應(yīng)用程序。例如,可以在頁面流中使用一個簡單的注釋來聲明一個Web service控件,如下所示: @Control
public MyWebService myWebService;
環(huán)境會自動將控件插入頁面流中。
與XMLBeans相結(jié)合,Beehive就可以提供無縫地構(gòu)建和部署企業(yè)SOA的基礎(chǔ)架構(gòu)。Beehive 1.0預(yù)定在本月底發(fā)布,2.0的規(guī)劃正在進(jìn)行中。可以使用諸如BEA WebLogic Workshop之類的IDE或Pollinate項目中開發(fā)的Eclipse插件來開發(fā)Beehive應(yīng)用程序。
結(jié)束語
頁面流非常堅定地承諾要使企業(yè)軟件的開發(fā)和維護(hù)變得更加輕松快捷。在本文中,我們介紹了頁面流超越Struts的一些優(yōu)點、頁面流如何增強現(xiàn)有的Struts應(yīng)用程序,以及可以用于升級和無縫構(gòu)建企業(yè)級SOA業(yè)務(wù)應(yīng)用程序的幾種方法,包括Struts互操作性和Struts合并。
參考資料
原文出處
http://dev2dev.bea.com/pub/a/2005/07/pageflows.html