西北工業大學網絡與控制研究所 于彩榮 戴冠中 朱正超 | |
一、概述 模式是用來描述所交流的問題及其解決方案。簡單的說,模式可以幫助我們在一個特定的環境中整理并記錄已知的可重制的問題及其解決方案,并且通過模式來與他人交流這些知識。模式的目标就是
提倡随着時間的推移,在概念上進行重用。 通常情況下,模式盡管有時不易了解,但使用卻非常簡單。模式是實踐的總結,他提供的解決方案是經過了在不同時間、不同項目中仿佛解決了相似的問題後才最終确定的。是以,模式提供了強大的可重用機制,避免了開發者和設計者的重複投資。 J2EE為我們提供了大量的模式,但是,沒有一個模式是獨立的實體,每個模式都存在着膽量的複雜關系,并且這些關系有時又被作為模式語言的一部分。是以,開發者不應僅僅了解存在于孤立環境中的離散模式,而應當去尋找最佳的實踐方式,機如何将這些模式連接配接在一起形成更完善的解決方案。以這種最佳方式連接配接多個模式就是所謂的權衡J2EE模式構架。構架就是将模式連接配接在一起形成的滿足一定需求的解決方案。是以,我們需要 ○ 确定場景并提供應用與每一層的模式 ○ 确定模式組合或者主題,以提供模式構架 與傳統的二層體系結構相比,J2EE有兩個特點: ○ 定義了一套标準化元件,通過為這些元件提供完整的服務。 ○ 使用多層分布式的應用程式模型。應用程式的邏輯根據其實作的不同功能被封裝到不同的元件中。元件的位置取決于元件本身在J2EE中所處的層次,如圖1所示。 ![]() 這種多層結構使企業級應用具有很強的伸縮性,允許各層專注于某種特定的角色: ○ Client Tier用于顯示。在典型的Web應用中,用戶端機器上運作的浏覽器負責實作使用者界面。 ○ Web Tier用于生成動态顯示。這一層采用WEB元件,它包括servlet和 JSP頁面:Servlets是一個Java類,可以動态地處理請求并作出響應;JSP頁面是一個基于文本的文檔,以servlet方式執行,但是它可以更友善地建立靜态内容。是以,通常用servlet 來控制流轉,Jsp來負責顯示生成的内容。 ○ Business Tier用于實作業務邏輯。 這一層采用EJB元件的Session Bean和Entity Bean。 Session Bean的主要目的是讓程式開發者将邏輯層抽離,特别是複雜的邏輯可以放在其中; Entity Bean是持久資料的對象表示,持久資料存儲在諸如資料庫等持久資料存儲中。 ○ EIS Tier用于資料庫服務。 本文認為客戶層和資源層不是J2EE平台直接關注的問題,是以重點介紹Web層和Business層。在Web層,本文采用了前端控制器模式和視圖助手模式;在Business層,本文着重于Session Façade模式和值對象模式。 二、Business Tier的設計 值對象模式 首先,為資料庫中的每個表建立一個實體Bean,并采用CMP(容器管理持久性)模式。 使用CMP的好處在于容器提供公共的服務,例如目錄服務、事務管理、安全性、持久性、資源緩沖池以及容錯性等,使開發人員不必維護将會內建到業務邏輯中的系統級代碼,隻需專注于商業邏輯。 J2EE應用程式把伺服器端業務元件實作為會話Bean和實體Bean,在這裡是Entity Bean。業務元件的一些方法可以向用戶端傳回資料。通常,用戶端需要多次調用業務對象的get方法直到獲得所有的屬性值,而且每次調用都是一次網絡調用,都會造成系統性能的退化,當調用次數增多時,系統性能下降的很厲害。如圖2-1。 值對象模式有兩種政策――可更新的值對象政策和多值對象政策。 可更新的值對象政策中,業務對象負責建立值對象,并且在用戶端請求時把該值對象傳回給用戶端;同時,業務對象也可以從用戶端接收資料,形成值對象,并使用該對象來完成更新。 例如,在銀行系統的例子中,Account 中提供一個以AccountValue為參數的setAccountValueObject方法,這樣用戶端可以通過這個方法來設定值對象的值,而不采用實體bean--Account中設定每個屬性的方法(setBalance()),因為後一種方法會導緻大量的網絡負載。由于值對象的易變性,是以值對象類必須給每個可以被用戶端更新的屬性提供設定方法。例如,AccountValue中的setBalance()方法。這樣,一旦某用戶端擁有來自業務對象的值對象,用戶端就可以在本地調用必要的設定方法來更改屬性值,然後調用業務對象的setAccountValueObject()方法更新業務對象。 多值對象政策 一些應用程式業務對象往往比較複雜,在這種情況下,根據用戶端請求不同,有可能單個業務對象會産生多個不同的值對象。在這種情況下,可以考慮采用多值對象政策。這種政策的實作比較簡單,就是在entity bean中增加不同的Get×××ValueObject()方法和set×××ValueObject()方法。 Session Façade 模式 有了實體Bean,用戶端就可以直接調用它以獲得資料。也就是說實體Bean封裝了業務資料,并把他們的接口暴露給客戶,因而也就把分布式服務的複雜性暴露給客戶。在對J2EE 應用程式環境下,一般會産生如下問題: ■ 緊密耦合,這回導緻用戶端和業務對象的直接依賴關系 ■ 用戶端和伺服器之間的網絡方法調用太多,容易導緻網絡性能問題 ■ 缺乏統一的客戶通路政策,容易誤用業務對象 ■ 如果實體bean的API改動,那麼使用者端的一些代碼也要修改,擴充性很差 解決這些問題的方法就是把用戶端和實體bean分割開。本文采用Session Facade模式,如圖3-2所示。該模式通過一個Session Bean,為一系列的實體bean提供統一的接口來實作流程。事實上,用戶端隻是使用這個接口來觸發流程。這樣,所有關于實體bean實作流程所需要的改變,都和用戶端無關。當實體bean改變時,我們不用改變用戶端的代碼,隻要對Session Bean作出相應的改變即可,大大提高了系統的可維護性。 通過實體bean來表示業務對象是session façade的最常見用法。但多個實體bean參與某用例時,不必向客戶暴露所有實體bean。相反的,用session bean 包裝這些實體bean ,并且提供粗粒度方法來執行所需的業務功能,進而隐藏了實體bean互動的複雜性。 但是千萬不要以為Façade模式就是簡單的用Session Bean把Entity Bean的所有方法統統封裝起來,而不提供任何額外的抽象。其實這是對Façade模式的濫用。這樣做并不是降低整個系統的複雜性,而是把複雜性轉移到另一個對象上。 正确應用Façade模式應遵循三條基本原則: ○ 他們自己不作實際工作,而是委派其他對象作實際工作。 ○ 他們提供簡單的接口。 ○ 他們是底層系統的用戶端接口。他們應該把特定于子系統的資訊封裝起來,并且不應該在不必要的情況下公開它。 例子代碼 下面用一個簡單的銀行系統的例子來解釋Façade模式和Value Object模式的具體應用。 √ 建立Entity Bean。其中對每個屬性的get和set方法是自動生成的,我們不去管它。 public interface Account extends javax.ejb.EJBObject { java.lang.String getAccountNumber() throws java.rmi.RemoteException; double getBalance() throws java.rmi.RemoteException; void setBalance(double newValue) throws java.rmi.RemoteException; private AccountValue creaeAccountValueObject(); void setAccountVauleObject(AccountValue v); AccountValue getAccountValueObject(); …… } 其中 private AccountValue createAccountValueObject(){ AccountValue vo=new AccountValue(); vo. accountNumber=accountNumber; Vo.balance=balance; …… } public AccountValue getAccountValueObject(){ return createAccountValueObject(); } public void setAccountValueObject(AccountValue v){ accountNumber=v. accountNumber; balance=v.balance; …… } √ 用值對象封裝Entity Bean資料。 public class AccountValue implements java.io.Serializable { private java.lang.String accountNumber; private double balance; void setBalance(double newValue) …… } √ 用Factory或者是Action類邏輯方法,涉及到資料的地方使用值對象。 public class AccountFactory { private static AccountHome accountHome = null; …… } public java.util.Vector getAccounts(String userid) throws FactoryException { try { Vector vect = new Vector(); AccountHome home = getAccountHome(); Enumeration accountRefs = home.findByUserid(userid); while (accountRefs.hasMoreElements()) { Account acc = (Account)accountRefs.nextElement(); AccountValue valueObject =acc.getAccountValueObjcet(); vect.addElement(valueObject); } return vect; } catch (Exception e) { throw new FactoryException(“Cannot generate accounts due to wrapped exception " + e); } } √ 在Session Bean的方法中調用Factory或者是Action對象。 public interface AccountSes extends javax.ejb.EJBObject {//Session bean 的遠端接口 java.util.Vector getAccounts(java.lang.String userid) throws java.rmi.RemoteException,com.ibm.bankexample.domain.FactoryException; …… } public java.util.Vector getAccounts(String userid) throws FactoryException { AccountFactory fact = new AccountFactory(); Vector result = fact.getAccounts(userid); return result; } 正如代碼所示,使用session façade模式,可以 ○ 提供統一的接口:會話外觀抽象了業務元件互動的複雜性,并且向用戶端提供一個更簡單的接口。 ○ 減少耦合提高可管理性:會話外觀分離了業務對象和用戶端,這樣可以減少緊密耦合,以及用戶端對業務對象的依賴性。 ○ 提供粗粒度通路:會話外觀減少用戶端和伺服器之間的網絡負載。用戶端與業務資料的說有互動都是通過會話外觀以粗粒度的發拿過是進行的。 此外,本論文也采用其他的小模式來提高系統性能,比如伺服器位器模式,在此不作進一步介紹。 三、Web Tier 在建立伺服器端Web應用程式時,将表現層與業務邏輯分離可以更容易的建立動态的Web頁面,同時也可以讓沒有應用開發經驗的Web頁面設計人員能非常容易的改變Web站點的外觀。對于一個内容需要頻繁更新的Web站點,這就意味着更新周期更短,可以以最快的速度帶給本站點的通路者以最新的内容。 早期的Web應用程式結構都很簡單,通常使用者界面與業務邏輯都混合在一起。修改這種應用的任何一方,都将使維護整個應用程式變得十分困難。Servlet和JSP技術能夠隔離使用者界面與業務邏輯,進而簡化應用程式的維護,使程式員可以更快更容易地改變應用程式。 從理論上講,使用者可以隻使用Servlet來接收從Web浏覽器發來的HTTP請求,Servlet動态地處理請求,然後直接發送HTML或XML文檔的響應給浏覽器。這種方法原理上可行,但是如果要改變頁面的外觀就必須重新編輯、編譯Servlet。這不僅要求動态Web頁面的設計人員需要有應用程式開發經驗,而且使程式難以維護。 我們采用前端控制器模型和視圖助手模型來解決這個問題。在前端控制器模型的實作上,我們采用了servlet前端政策。該政策把控制器實作為一個servlet。控制器負載與業務處理和控制流有關的請求處理。在後者的實作上,本文采用Jsp視圖政策。就是使用Jsp作為視圖元件,Jsp僅負責顯示而沒有任何業務處理。 繼續采用銀行系統的例子如下: √ 用戶端浏覽器發起的請求直接到了servlet。 <FORM action="/LoginServlet" method="POST"> <input type="text" size="30" name="UserId"> <input type=submit value=" Submit "> </FORM> √ Servlet調用Session Bean 方法查詢結果,然後将結果放入session中,調用JSP來處理這個響應。 java.util.Vector rs=new Vector(); HttpSession session=request.getSession(true); String id=request.getParameter("UserId"); Context ic=new InitialContext(); Object obj=ic.lookup("AccountSes"); acc=(AccountSesHome)PortableRemoteObject.narrow(obj,AccountSesHome.class); rs=acc.getAccounts(id); session.setAttribute("rs",rs); getServletContext().getRequestDispatcher("/display.jsp").forward(req,res); JSP決定産生給使用者響應的内容,它隻包含如何格式化表現層的邏輯。 <TR> <TH>AccountNum </TH> <TH>Balance </TH> </TR> <% Vector rs=new Vector(); AccountValue valueObject = new AccountValue(); rs=( Vector)session.getAttribute("rs"); while(!rs.isEmpty()) { valueObject=rs.remove(0);%> <TR> <TD><%=valueObject.getAccountNumber ()%></TD> <TD><%= valueObject.getBalance()%></TD> </TR> 隔離Servlet開發與JSP顯示為應用開發人員和Web頁面設計人員彼此獨立的工作帶來了極大的友善。 總結 綜上所述,本文系統地闡述了基于J2EE的3層B/S系統的建構方法, 綜合應用了EJB,Servlet,Jsp技術,融合了幾種不同模式,很好的實作了資料封裝,合理分層以及顯示與邏輯分離,大大提高了系統的可維護性和可伸縮性,也顯著的簡化了具有可伸縮性和高度負責的企業級應用的開發。 |