摘要:
利用JSF框架在表示層上的強大功能,針對JSF框架在業務、數據持久化管理方面的不足,采用MVC(model-view-controller)model-2模式設計一種Web應用開發方案。該方案采用表示層、業務層、持久層三層結構進行設計。工程實踐表明,利用該方案進行Web開發有結構清晰、維護方便和高復用性等優點。
關鍵詞:MVC model-2模式; JSF(JavaServer faces)框架; 業務層; 持久層
中圖分類號:TP311.5文獻標志碼:A
文章編號:1001-3695(2007)12-0272-04
0引言
網絡信息為企業提供了種種機遇和廣闊的發展空間,Web應用平臺的建立成為商業企業的制勝法寶。傳統基于J2EE的Web開發模式存在著顯示與業務邏輯高度耦合、后期維護困難、軟件難以復用等問題[1]。為解決這些問題,近年來出現許多基于J2EE的Web框架[2],即JSF、Struts、WebWork、Spring和Tapestry等。在眾多框架中JSF具有獨特優勢[3]:a)標準化。很多框架可供選擇是有很多益處,但也可能會分裂Java開發社區,JSF為Web應用開發提供了一種標準。b)強工具支持。一個標準的Web應用框架,同時有良好文檔化的API,使得所有基于Java的IDE或其他工具會更容易做出對JSF的支持。c)組件化。JSF提供了可重用并可擴展的用戶界面組件,使得Web頁面的開發更方便、快捷。但是JSF的側重點均在表示層上,對于業務、數據持久化管理沒有統一規范。當然可以使用EJB或者集成Hibernate框架進行數據持久化。但這樣也把EJB的復雜性以及框架集成所出現的新問題引了進來,給Web開發帶來了難度[3~5]。針對這一問題,本文設計了一種基于JSF的Web應用開發方案。該方案采用三層結構(表示層、業務層、持久層)進行設計,充分利用了JSF在表示層上的強大功能,補充了JSF在業務層和持久層方面考慮的不足。經實例驗證,利用該方案進行Web應用開發層次結構清晰、人員分工明確、后期維護方便、程序復用率高。
1MVC model-2模式和JSF框架
1.1適用于Web的MVC model-2[3]模式
傳統的MVC模式旨在將交互性應用分為三個單獨的組件[3],即model(模型)、view(視圖)和controller(控制器)。Model-2模式是傳統MVC模式為適應Web所作的改編。這是由于Web應用依賴于超文本傳輸協議(HTTP)。Web下的model-2結構如圖1[3]所示。
用戶與Web頁面交互,并最終向服務器發送一個請求來獲取更多信息(如點擊一個按鈕)。設計和部署時就指定好的controller servlet偵聽特定的URL請求。當接收到一個請求,servlet和model進行交互,然后決定向哪個view組件(JSP頁面)派發控制以生成響應。為了生成響應,view組件可能會查詢model[3~5]。
1.2JSF框架
JSF規范是由JCP(Java community process)下的JSR-127專家組開發的[3]。它通過提供以下特性來加速開發進程[4]:標準可擴展的用戶界面組件、易配置的頁面導航、方便的數據驗證和轉換、自動化Bean管理、事件處理、方便的錯誤處理以及內置對國際化支持。JSF提供了對model、view、controller的明確定義[3~5]:
a)Model。它是進行業務操作的部分,用來實現業務邏輯。一般使用JavaBean或者EJB(enterprise JavaBean)來建立復雜的企業應用。
b)View。由JSF標簽構成的JSP頁面組成,通過一個字符串松耦合到controller。View通過發送事件來間接調用controller的邏輯。
c)Controller。主要包括FacesServlet、配置文件和action處理器。FacesServlet負責從客戶端接收請求,然后執行一組合理步驟(重建組件樹、應用請求值、處理驗證、更新模型值、調用應用、呈現響應)來準備和派發響應。
隨著JSF的引入,可以簡單地通過一組可重用組件創建用戶界面。針對JSF在業務、數據持久化管理方面的不足,筆者設計了業務層、持久層專門用來處理業務和數據持久化管理,以方便用戶開發。
2基于JSF框架應用開發方案的設計
該方案采用了三層結構,即表示層、業務層和持久層。表示層主要用來構建用戶界面、控制頁面轉發、響應用戶請求并調用相應業務邏輯進行處理;業務層封裝了整個系統的業務邏輯;持久層也稱為數據庫訪問層,直接操作數據庫并對用戶輸入的數據(后面簡稱為輸入數據)進行持久化或直接從數據庫中提取數據交給業務層處理。
2.1設計方案的體系結構
如圖2所示,JSF的view和controller均在表示層,且在本方案中還作為model-2模式的view和controller部分,方案中的業務層和持久層作為model-2模式的model部分。
FacesServlet負責接收用戶請求。如果不需要對數據驗證處理,它會跳過生命周期的其他階段轉到view進行渲染響應;反之,則會調用相應驗證器驗證,調用相應action進行處理。Action處理器會把用戶數據暫存到業務層的實體域對象[6](由JavaBeans組成,以后詳細介紹)中,并調用相應service方法進行處理,若需要對數據持久化,service方法會調用持久層中持久化方法進行持久化,facesServlet會根據action的處理結果,結合配置文件跳轉到view部分進行渲染響應。
2.2表示層設計
該層包括model-2模式中view和controller兩大部分[3]。這兩部分分別延用了JSF的view和controller設計。
2.2.1View部分設計
View部分由JSP頁面和資源文件組成。主要功能有:構建用戶界面、為UIInput組件注冊驗證器和轉換器以便在后續工作中對數據進行驗證和轉換、配置資源文件以支持國際化、注冊事件監聽器以監聽用戶動作并在后續工作中作出處理。
View中頁面由JSF標簽(用戶界面組件)構成。一個頁面的所有JSF標簽組成了一個組件樹。JSF有兩種標簽庫,即核心標簽和HTML標簽[5]。例如:創建一個文本輸入框標簽(已綁定值):〈h:inputText id=“name”value=“#{loginAction.username}”/〉。其中loginAction為受管Bean,在controller中創建,它包含了已實現setter和getter方法的username屬性,通過值綁定表達式來接收輸入數據或把model中數據呈現給用戶。
有兩種驗證方式驗證輸入數據[3]:在UIInput組件的直接驗證和在UIInput組件之外的托管驗證。直接驗證適用于要執行的驗證是特定于具體UIInput組件,可以通過重載validate()方法將驗證代碼在組件內部實現;托管驗證適用于要執行的驗證能被許多不同類型的用戶界面組件重用,可通過方法綁定或Validator組件來完成。使用時需將驗證器標簽嵌套在UIInput標簽內進行注冊。
有兩種轉換方式轉換輸入數據[3],即標準轉換器和自定義轉換器。標準轉換器可直接使用,使用時把標準轉換器標簽嵌套在UIInput標簽內進行注冊;自定義轉換器要實現javax.faces.convert.Converter接口。該接口有兩個方法,即getAsObject()和getAsString()。使用時,先把它在配置文件faces-config.xml中進行注冊,然后才能像使用標準轉換器一樣使用。
View中通過創建屬性文件以支持國際化[5],如創建默認語言屬性文件messages.properties,然后向該文件中添加相應鍵值。使用時需向頁面中添加f:loadBundle元素,然后可以通過值綁定表達式訪問字符串。若支持其他語言,需創建支持該語言的屬性文件,文件名:原默認屬性文件名加下畫線后跟兩個小寫字母(ISO-639語言編碼)。例如支持德語,需創建屬性文件messages_de.properties。
View中頁面可響應兩種類型事件[3],即值變換事件(value changed)和動作事件(action)。值變換事件用于觀察用戶界面組件值的改變;動作事件用于觀察用戶觸發的動作。應用時需實現監聽器并注冊監聽器。
有兩種方式實現監聽器[3]:一是直接在后臺Bean中添加監聽器方法,要遵守這樣約定:返回void,接收一個事件類型的參數(所注冊事件);二是自定義監聽器,要實現actionListener或valueChangeListener接口,每個接口定義了一個單獨方法。注冊監聽器時,需把監聽器方法綁定到UICommand組件的actionListener或valueChangeListener屬性上。
2.2.2Controller部分設計
Controller由facesServlet、配置文件以及action處理器組成[3]。主要功能有:控制頁面導航、響應用戶請求并處理、接收輸入數據并暫存到業務層的實體域對象中或從業務層提取數據給view。
控制頁面導航是controller的主要功能。首先實現應用action(通過方法引用表達式綁定到UICommand中action屬性的方法,被稱為應用action),JSF在運行時使用反射來定位和執行應用action。該action方法可在受管Bean[5]中添加,要遵守這樣的約定:公有方法,沒有參數,返回string類型值。應用時把action方法或字符串綁定到UICommand組件的action屬性上[3]。
下面在faces-config.xml中指定導航規則,from-view-id元素定義了動作來源頁面,可以去掉該元素,那樣允許所有頁面訪問該規則。From-outcome元素定義了應用action一個可能出口(返回值)。如果動作返回了匹配from-outcome的字符串,框架將把頁面導航到由to-view-id元素定義的頁面[3]。代碼類似如下:
〈navigation-rule〉
〈from-view-id〉/login.jsp〈/from-view-id〉
〈navigation-case〉
〈from-outcome〉success〈/from-outcome〉
〈to-view-id〉/loginsuccess.jsp〈/to-view-id〉
〈/navigation-case〉
〈/navigation-rule〉
下面在faces-config.xml文件中創建受管Bean。其作用是接收輸入數據并存入業務層的實體域對象中、調用業務邏輯以及幫助動態導航。其方法和屬性可直接綁定到用戶界面組件上。創建時要指定該Bean的作用范圍,如創建范圍為request的loginAction為受管Bean,需寫如下代碼:
〈managed-bean〉
〈managed-bean-name〉loginAction〈/managed-bean-name〉
〈managed-bean-class〉LYEIMApplication.login.loginAction〈/mana-ged-bean-class〉
〈managed-bean-scope〉request〈/managed-bean-scope〉
〈/managed-bean〉
2.3業務層設計
業務層由實體域對象包和封裝了業務邏輯的JavaBeans組成。主要功能有:暫存輸入數據或持久層中數據、實現業務邏輯、被動接收controller的調用或主動調用持久層中方法幫助數據持久化。
實體域對象包由JavaBeans組成。一個JavaBean對應著數據庫中一張表,這些JavaBeans只包含與相應表中字段相對應的屬性,并且都實現了setter和getter方法。主要用來暫存輸入數據或從持久層中提取的數據。
封裝了業務邏輯的JavaBeans在系統中起核心作用,它封裝了整個系統的業務邏輯,也起著調用持久層中持久化方法的作用。系統開發時,可以按功能分類,每一類用一個JavaBean封裝其相關業務邏輯。如筆者所做的工業科技信息上報系統中的EnterpriseInstInputService.java就封裝了與企業基本信息錄入相關的業務邏輯。該Bean中有如下方法:
public void EnterpriseInstInputProcess (EnterpriseBasisTable obj) throws SQLException
//輸入處理,調用持久層中持久化方法幫助數據持久化
public String getEnterpriseStyleName(int id) throws SQLException
//得到企業類型名稱
public List getOneStyleEnterprise(int style)throws SQLException
//得到某一類型企業
public List getOneEmployeeWithOneEmployeeID(int id) throws SQLException//傳遞一個員工ID得到該員工的基本信息
該層中所有與持久層交互的方法均會傳遞一個connection對象,且調用完畢后關閉該對象,在該層可對JDBC事務進行處理,如一次可以向多個表中插入數據。系統開發時,可建一個business包作為業務層,再建service和domainObject兩個子包封裝業務邏輯和實體域對象。
2.4持久層設計
持久層由封裝了持久化方法的JavaBeans組成,主要功能有:直接與數據庫交互,從數據庫中讀取數據交給業務層處理或從業務層接收數據存入數據庫。
一般來講,一個持久化Bean對應著數據庫中一張表,也可以專門建一個持久化Bean封裝所有的復合查詢。持久化Bean中封裝了訪問數據庫的常用方法:
a)插入(insert)。傳遞一個實體對象,向相應表中插入一條記錄。
b)刪除(delete)。傳遞一個參數(表ID、字符串等),從相應表中刪除一條記錄。
c)更新(update)。傳遞一個參數(表ID、字符串等)和實體對象,從相應表中更新一條記錄。
d)查詢(select)。傳遞一些參數,從相應表中提取一條或多條記錄。
例如一個持久化Bean:EnterpriseBasisTableDAO.java,該Bean中會有如下方法:
public void insert(EnterpriseBasisTable obj,Connection conn ) throws SQLException//向表中插入一條記錄
public void deleteWithOnetableID(int tableID, Connection conn)throws SQLException//從表中刪除一條記錄
public void updateWithOnetableID (EnterpriseBasisTable obj,Connection conn) throws SQLException//更新一條記錄
public ResultSet selectWithNothing(Connection conn) throws SQLException//從表中選擇所有記錄
public ResultSet selectWithOneEntepriseID(int id, Connection conn) throws SQLException//從表中選擇一條記錄
這些方法均在業務層中被調用,系統開發時,可建一個persist包作為持久層,再分類建立子包,往各包中添加持久化Bean。
3應用實例
為對該方案有一個更清楚的理解,下面以登錄模塊為例介紹具體開發過程,將該模塊作為一個單獨應用程序。
1)配置開發環境需安裝SQL Server 2000數據庫、JDBC驅動、JSF開發工具。可選擇Java Studio Creator2、eclipse+My eclipse、NetBeans 5.0等JSF開發工具。本示例用NetBeans5.0開發。該工具集成了Tomcat服務器。
2)View部分開發新建loginApp應用程序,選擇捆綁的Tomcat(5.5.9)服務器,新建或把已創建的index.jsp重命名為login.jsp,再建兩個顯示處理結果頁面,即success.jsp和failure.jsp。在login.jsp中添加文本輸入框、密碼輸入框和兩命令按鈕四個標簽,在src目錄下建一個messages.properties屬性文件以支持國際化(右鍵點擊該文件添加語言環境)。
3)持久層開發配置Tomcat數據庫連接池,在src目錄下建一個loginApp包作為業務層和持久層根目錄,在loginApp包中建一個persist包作為持久層,在該包中建一個連接類:connect.java,本程序用到數據庫中employee和login兩個表,所以需在loginApp.persist.login包中建兩個持久化Bean:employeeDAO.java的loginDAO.java,在Bean中添加插入、刪除、更新和查詢方法,本程序用到了loginDAO.java中selectUser方法。
4)業務層開發在loginApp包中建一個business包作為業務層。在該包中建domainObject和service兩個子包作為實體域對象包和業務邏輯包。在domainObject包中建兩個類文件,即employee.java和login.java。這兩個文件均具有與相應表中字段相對應的屬性,且實現了setter和getter方法。在service.Login包中建一個loginService.java封裝與登錄相關的業務邏輯,本程序用到了該Bean中verifyUser方法。其代碼如下(其中略去捕捉異常和關閉連接):
public boolean verifyUser(String username,String password) throws SQLException{
LoginDAO dao=new LoginDAO();
ResultSet rs=dao.selectUser(username,password,conn);
if(rs.next()) return true;
else reutrn 1;
}
5)Controller部分開發在src目錄下建一個controller包作為model-2模式中的controller部分,在controller.login包中創建action處理器:loginAction.java,在該文件中創建username和password兩個屬性,要實現其setter和getter方法,再把這兩個屬性綁定到login.jsp頁面的相應組件上,用來接收輸入數據。下面在loginAction.java中創建響應用戶動作的action方法,該action方法要滿足應用action的約定。其代碼如下(略去了捕捉異常和其他判斷細節):
public String verifyUser() throws SQLException{
LoginService login=new LoginService();//創建業務處理對象
if(login.verifyUser(this.username,this.password))
return \"success\";
else return \"failure\";
}
在faces-config.xml文件中設置loginAction為受管Bean。指定導航規則,為verifyUser方法的出口指定匹配頁面:“success”匹配success.jsp,“failure”匹配failure.jsp。
6)部署運行通過以上幾個步驟已完成登錄模塊的開發(省略了一些具體細節)。下面設loginApp為主項目,單擊“運行主項目”,NetBeans會自動把應用程序部署到它所捆綁的Tomcat服務器上運行。本程序的文件結構如圖3所示。
4結束語
利用本方案已完成了某市工業科技信息上報系統的開發。現在該系統正在使用,已得到用戶好評。通過分析和實例驗證,使用本方案做Web應用開發有以下優點:
a)表示層使用了JSF技術。它提供了豐富、可重用的用戶界面組件。在開發工具支持下,用戶很容易在可視化環境中利用這些組件構建Web用戶界面,處理組件的數據校驗、事件處理等用戶界面管理。
b)三層結構設計。使得系統層次結構清晰,業務邏輯與表示完全分離,開發人員分工明確,便于軟件的后期維護和復用。
c)業務層、持久層的設計。避免了使用EJB所帶來的復雜性和框架集成所出現的新問題,使得開發人員能夠快速上手進行系統開發。
但是本方案也有不足的地方,如持久層的生成,可以開發一個代碼生成器,根據數據庫把持久層自動化生成出來,以方便用戶開發。這將在以后的工作中作進一步的改進。
參考文獻:
[1]何明德,陳川,張硯秋.基于MVC設計模式構筑JSP/Servlet+EJB的Web應用[J].計算機工程,2001,23(11):71-73.
[2]FORD N. Art of Java Web development[M].[S.l.]:Manning Publications Co, 2004:133-311.
[3]DUDNEY B. Mastering JavaServerTM faces[M].孫勇,等譯.北京:電子工業出版社,2005:8-155.
[4]KURNIAWAN B. JavaServer faces編程[M].劉克科,等譯.北京:清華大學出版社,2005:322-333.
[5]GEARY D, HORSTMANN C. JavaServer faces核心編程[M].王軍,等譯.北京:電子工業出版社,2005:30-259.
[6]Spring0381. Java域對象持久化技術的比較[EB/OL].[2006-06-09].http://java.ccidnet.com/art/297/20060609/575857_1.html.
“本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文”