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