基於J2EE規範的TeaFramework框架(畢業設計用於學習J2EE寫的一個框架)

基於J2EE的框架技術的實現與應用

【摘要】 J2EE的應用越來越廣泛,J2EE應用從設計上可以分爲三層:表示層、業務層和數據持久層。在這三層上與之對應的J2EE技術爲WEB技術和EJB技術,WEB技術實現表現層,而EJB規範負責業務層和數據層。由於J2EE技術的不斷髮展,在J2EE設計的各個層次現在都有對應的輕量級框架出現,輕量級框架的發展極大了推動了J2EE技術的發展,輕量級框架技術已經成爲了下一代J2EE構架的基礎。 本次論文中,對J2EE技術和框架技術進行了研究,特別是J2EE輕量級框架技術的研究,在研究了J2EE技術的新發展和分析了一些輕量級框架(Struts2、Spring、iBatis)思想及源碼的基礎上提出了一個基於J2EE的輕量級框架TeaFramework,並給出了其各層相應的框架組件的設計,包括表示層MVC組件;業務層包括IoC組件;數據持久層主要是O/R映射組件。重點分析論述了MVC組件和業務層組件,並給出了MVC組件和業務層組件詳細的設計和實現。在論文的最後通過應用一個資產管理系統,實際檢驗了本論文研究的輕量級的通用的快速開發框架的可行性及穩定性,該資產管理系統的功能主要包括用戶管理、資產管理以及資產統計等。

目 錄1 緒 論 11.1課題的研究背景 11.2國內外現狀及趨勢 21.3課題的主要研究工作 22 相關技術分析 42.1 J2EE技術 42.1.1 J2EE的概念 42.1.2 J2EE的體系結構 42.2框架技術 52.2.1 框架出現的原因 62.2.2 框架的定義 62.2.3 框架的特點 72.3 輕量級框架的發展 73 輕量級框架體系結構設計 93.1 框架的設計分析 93.2 框架的總體設計 94 輕量級表現層框架設計與實現 114.1 Web應用系統傳統開發方式分析 114.2表現層MVC框架的設計 124.3表現層MVC框架的實現 134.3.1 框架的初始化 134.3.2 入口過濾器的實現 154.3.3 數據流體系的實現 174.3.4 控制流體系的實現 184.3.5 一次完整請求處理的實現 204.4 MVC框架所涉及的設計模式 224.4.1 ThreadLocal模式 224.4.2攔截器模式 234.4.3策略模式 234.5 MVC框架配置及使用說明 245 輕量級業務層框架設計與實現 265.1 IoC模式的分析 265.1.1 IoC模式的產生 265.1.2 IoC模式與工廠模式 265.1.3 IoC組件產生的意義 275.2業務層 IoC框架的設計 285.2.1 IoC容器的總體設計 285.2.2 IoC框架的關鍵技術 295.2.3 IoC框架在TeaFramework中的作用 305.3 業務層IoC框架的實現 305.3.1 BeanDefinition的載入與解析 315.3.2 BeanDefinition在IoC容器中的註冊 325.3.3 Bean的實例化及依賴注入的實現 335.3.4 TeaIoC整體核心類圖 355.3.5從BeanFactory得到Bean的流程 365.4 IoC框架與MVC框架的整合實現 375.5 IoC框架配置及使用說明 386 輕量級數據持久層框架設計與實現 406.1 數據持久化技術分析 406.1.1 JDBC技術 406.1.2數據持久層分析 406.1.3全自動ORM與半自動ORM技術的比較 416.2 數據持久層ORM框架的設計 426.3 數據持久層ORM框架的實現 436.3.1 TeaORM的實現原理 436.3.2 SQL語句的解析 436.3.3 Statement的實現 446.3.4 Parameter輸入參數的實現 456.3.5 ResultMap輸出結果的實現 466.4 IoC框架與ORM框架的整合實現 476.5 ORM框架配置及使用說明 487 輕量級TeaFramework框架的應用 507.1 系統分析 507.1.1 資產管理系統簡介 507.1.2 資產管理系統功能介紹 507.1.3 資產管理系統數據分析 517.2 系統設計 517.2.1 系統總體結構設計 517.2.2 數據庫設計 527.3 系統實現 547.3.1 TeaFramework在資產管理系統中的應用 547.3.1.1 TeaMVC與Web環境的整合配置 547.3.1.2 TeaMVC的應用配置 557.3.1.3 TeaMVC攔截器實現登錄及權限驗證 567.3.1.4 TeaORM的應用配置 587.3.1.5 TeaIoC的應用配置 597.3.1.6 TeaIoC和TeaMVC的整合配置 607.3.1.7 TeaIoC和TeaORM的整合配置 617.3.2.系統功能模塊的設計與實現 617.3.2.1 資產管理系統工程包層次結構 617.3.2.2 資產添加模塊的設計與實現 627.3.2.3 資產查詢模塊的設計與實現 647.4 利用TeaFramework實現資產管理系統的效果 67 1.1 課題的研究背景J2EE本身就是一個標準的集合,是一個標準的WEB應用。之所以使用框架,從根本上來說還是爲了理清程序邏輯和程序結構,減輕程序員的開發強度,讓程序員更加註重業務的開發。J2EE框架標準將一個系統劃分爲WEB和EJB兩個主要部分,從設計上可以抽象爲表現層、業務層和持久層,這三個層次從一個高度將J2EE分離開來,實現瞭解耦的目的。因此,在實際編程中大多數應用從功能上根據這三個層次來劃分,但要這樣做,沒有技術上約束限制是比較困難的,因此一般藉助J2EE具體技術來實現,可以使用EJB規範來實現服務層和持久層,Web技術來實現表現層。J2EE應用雖然從總體上劃分了三個層次,但在針對每一層上的具體應用,要設計出可維護性、高拓展性的軟件設計目標還是有一定的難度。首先,Web層的技術主要是JSP/SERVLET,在這層仍然可以表現業務邏輯和數據訪問,那麼其後果是WEB層與後臺邏輯過於耦合,造成多層結構開發無法分工合作;其次,EJB被認爲是一種重量級的高度侵入性的框架規範,重量級在於它的基於分佈式的應用,高侵入性是指它制定了衆多的接口和編碼規範,要求實現者必須遵從;重量級的後果是部署複雜、運行慢、測試困難和代價高等,侵入性的後果就是一旦系統基於侵入性框架設計開發,那麼之後任何脫離這個框架的企圖都將付出極大的代價。輕量級框架的發展已經成爲推動J2EE技術發展的重要推動力,目前,輕量級框架的發展非常的快,同一類型的框架不斷有新框架推出,原因在於各個框架都不是很完美。J2EE框架研究與實現也是基於這樣幾個出發點:(1)現在一些優秀的開源框架的設計理念很多是值的學習及研究的,一些好的的設計可以應用到自己定製的輕量級框架中。(2)不是所有的框架都適合每個項目的應用,很多時候我們只需要應用其中的部分功能,而有時開發一個項目時需要多個框架的集成。因此,在開發項目時需要定製適合自己項目的輕量級框架。(3)在框架的應用前還是要了解框架本身的技術實現,在實現項目開發的輕量級框架過程中,既有技術基礎的積累,又能便於框架的定製和推廣。(4)現在的很多框架只是集中在一個層次解決J2EE的應用問題,定製的輕量級框架可能是一個多層次的解決方案。(5)希望實現的輕量級框架能成爲比較實用和通用的軟件開發半成品框架或平臺,能迅速地實現軟件的構建從而提高軟件的生產率。    1.2 國內外現狀及趨勢針對重量級框架中的問題,近年來,輕量級框架的發展非常的繁榮,比較成功的有在Web層出現了許多通用的Web開發框架如Turbine,Struts,Webx等,爲Web應用開發提供了便利。其中Struts是一個典型的MVC模式的Web開發框架,也是一個應用非常廣泛的Web框架,應用Struts2框架可以開發出基於MVC設計模式的Web應用構架。在業務層非常成功的主要有Spring和Hibernate框架,這兩個框架聯合起來的作用相當於在J2EE體系結構中的EJB組件發揮的作用。Spring是一個以反向控制(IoC Inversion of Control)爲基礎的輕量級框架。其設計目標是提供一種無侵入式的高擴展性構架。即無需代碼中涉及Spring專有的類,即可將其納入Spring容器進行管理。而Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,使得JAVA程序員可以隨心所欲的使用對象編程思維來操縱數據庫。不斷完善的Hibernate可以在應用EJB的J2EE架構中取代CMP完成數據持久化的重任。人們日益對開源框架的重視,使得很多項目的成本大大降低,並且投放使用以及維護速度都增加了。現在的開源框架都有很高的質量,都提供了很好的文檔&一些書籍讓開發者做參考。即便如此,兩大因素是的J2EE領域充滿了不確定性:開源領域和J2EE“標準”的衝突和AOP的日益重要。開源和標準之間的衝突表現在兩個地方。一個是表現層,JSF的身後有Sun公司和其他的一些大公司,而在這個領域有Struts等開源產品與之競爭。在中間層,EJB 3.0採用J2SE5.0的annotations實現了依賴注入(dependency injection)的功能,但這個功能只是Spring的一個子集。在這兩個領域,開源產品都有了革新:JSP借鑑了ASP .NET,而Tapestry則採用了WebObjects的思想。於此同時,AOP的重要性在J2EE社區猛增,在使用上。AOP也越來越受到開發者的青睞。像Spring框架的實現提升了AOP的知名度,而純粹的AOP技術比如AspectJ,在將來的幾年也會流行起來。下一代的J2EE規範將擁抱更簡單的POJO編程模型,就像Spring和Hibernate做的一樣。J2EE開發者也註定要將焦點轉到以自己的編程經驗開發上來。1.3 課題的主要研究工作本次設計的目標是實現一個基於現在主流框架思想的快速開發J2EE項目的的軟件框架或者是軟件平臺TeaFramework,這個框架抽取了大多數的J2EE項目中設計(非業務邏輯)中的同類問題、性能問題和安全問題等解決方案,主要工作主要集中在:(1)技術分析。分析J2EE技術和框架技術的發展,特別是J2EE輕量級框架技術的特點和發展,闡述了J2EE技術設計的多層結構,及各對應層的功能和特點。(2)TeaFramework體系結構設計。先從整體上提出TeaFramework的架構,將其層次劃分爲三層結構,即表示層、業務層和數據持久層結構,設計對應每層的主要框架組件,包括MVC組件,IoC組件,O/R映射組件等。(3)框架實踐。在論文的最後通過一個資產管理系統來實踐TeaFramework框架,並充分說明利用該框架開發實際應用軟件的作用及意義。(4)設計總結。綜合論述從總體上對所做的工作進行了總結並指出了論文存在的不足以及後繼工作的展望。(5)特別說明。本文的多層次的框架的設計思想和設計理念都來源於現在主流的開源框架,前期對許多優秀開源框架做了深入的研究。TeaMVC的實現理念來源於Struts2框架,TeaIoC的實現理念來源於Spring框架,TeaORM的實現的理念來源於iBatis框架,只是實現了部分核心功能,目的是整合它們的核心功能,作爲開發工具,能快速及有針對性地開發軟件,並且能在以後定製項目需要的功能。論文的新穎之處在於:(1)本論文的框架整合了一些流行的開源框架的設計思想,各個層次間不需要額外的插件,能作爲一個簡易的開發平臺,快速地開發軟件。(2)本論文一大亮點是對每個層次組件之間的整合實現,就是通過互相整合,最後把單一的組件框架的實現整合成一個完整的能夠工作的整合框架。(3)框架技術的發展大多是目前比較新興的技術,論文本身研究的框架內容如:MVC,IoC和O/R映射都是比較新穎的J2EE技術,是下一代J2EE技術的基礎。(4)TeaFramework框架的設計從整體上設計了J2EE分層結構的各層(WEB層、業務層以及數據持久層)的解決方案。(5)本論文不僅是對新興的框架技術作研究,重點是都實現了這些J2EE技術如:MVC和IoC技術,爲實現軟件工廠目標積累了技術基礎。(6)論文的最後通過一個資產管理系統來實踐了本論文研究的輕量級的框架,可以充分說明TeaFramework框架對應用系統開發的作用及意義。2 相關技術分析2.1 J2EE技術J2EE(Java2 platform Enterprise Edition)是SUN 公司提出的在分佈式環境中的一種體系結構,它提供了一種基於組件的設計、開發、集成、部署企業應用系統的方法。J2EE平臺提供了多層分佈式的應用系統模型,重用組件的能力、統一的安全模型和靈活的事務控制。基於組件的J2EE企業應用系統具有平臺獨立性,所以不受任何軟件產品和任何軟件廠家API的約束。2.1.1 J2EE的概念J2EE是一種利用Java 2平臺來簡化企業解決方案的開發、部署和管理相關的複雜問題的體系結構。J2EE技術的基礎就是核心Java平臺或Java 2平臺的標準版,J2EE不僅鞏固了標準版中的許多優點,例如“編寫一次,隨處運行”的特性、方便存取數據庫的JDBC API、CORBA技術以及能夠在Internet應用中保護數據的安全模式等等,同時還提供了對 EJB(Enterprise JavaBeans)、Java Servlets API、JSP(Java Server Pages)以及XML技術的全面支持,其最終目的就是成爲一個能夠使企業開發者大幅縮短投放市場時間的體系結構。J2EE體系結構提供中間層集成框架用來滿足無需太多費用而又需要高可用性、高可靠性以及可擴展性的應用的需求。通過提供統一的開發平臺,J2EE降低了開發多層應用的費用和複雜性,同時提供對現有應用程序集成強有力支持,完全支Enterprise JavaBeans,有良好的嚮導支持打包和部署應用,添加目錄支持,增強了安全機制提高了性能。2.1.2 J2EE的體系結構J2EE使用多層的分佈式應用模型,應用邏輯按功能劃分爲組件,各個應用組件根據它們所在的層分佈在不同的機器上。事實上,Sun設計J2EE的初衷正是爲了解決兩層模式(Client/Server)的弊端,在傳統模式中,客戶端擔當了過多角色而顯得臃腫,在這種模式中,第一次部署的時候比較容易,但難於升級或改進,可伸展性也不理想,而且經常基於某種專有的協議――通常是某種數據庫協議。它使得重用業務邏輯和界面邏輯非常困難。現在J2EE 的多層企業級應用模型將兩層化模型中的不同層面切分成許多層。一個多層化應用能夠爲不同的每種服務提供一個獨立的層,以下是 J2EE 典型的四層結構:客戶層、WEB層、業務層及企業信息系統層(EIS)。J2EE體系結構圖如圖2.1所示。

圖2.1  J2EE體系結構圖從圖2.1中可以看出,一個基於J2EE的企業應用從結構上來看,可以分成四部分:分別是客戶層、WEB層、業務層和企業信息層。下面是對它們的描述:(1)客戶層(Client Tier)J2EE 應用可以是基於Web的,也可以是不基於Web的。在一個基於Web 的J2EE 應用中,用戶的瀏覽器在客戶層中運行,並從一個Web服務器上下載Web 層中的靜態HTML頁面或由JSP或SERVLETS生成的動態HTML頁面。(2)Web層(Web Tier)J2EE的Web組件可以由Jsp頁面、基於Web 的Applets 以及顯示Html頁面的Servlets組成。調用Servlets或者Jsp頁面的Html頁面在應用程序組裝時與Web 組件打包在一起,就像客戶層一樣,Web層可能包括一個JavaBeans類來管理用戶輸入並將輸入發送到在業務層中運行的EJB類來處理。(3)業務層(Busniss Tier)業務層經常被稱作EJB層。作爲解決或滿足某個特定業務領域(比如銀行、零售等)的需要的邏輯的業務代碼由運行在業務層的EJB來執行。一個EJB從客戶程序處接收數據,對數據進行處理,再將數據發送到企業信息系統層存儲。一個EJB 還從存儲中檢索數據,並將數據送回客戶程序。運行在業務層的EJB依賴容器來爲諸如事務、生命期、狀態管理、多線程及資源存儲池提供通常都非常複雜的系統級代碼。(4)企業信息系統層(Enterprise Information System Tier)企業信息系統層運行企業信息系統軟件,這層包括企業基礎設施系統,例如企業資源計劃(ERP),大型機事務處理、數據庫系統及其他信息系統。J2EE 應用組件因爲某種原因(例如訪問數據庫)可能需要訪問企業信息系統。J2EE平臺的未來版本將支持Connector架構,該架構是將J2EE平臺連接到企業信息系統上的一個標準API。2.2 框架技術伴隨着軟件開發技術的發展在多層的軟件開發項目中可重用易擴展而且是經過良好測試的軟件組件越來越爲人們所青睞這意味着人們可以將充裕的時間用來分析構建業務邏輯的應用,而非繁雜的代碼工程。於是人們將相同類型問題的解決途徑進行抽象抽取成一個應用框架,也就是說框架是一些經過實踐證明的、能用來開發高效應用系統的技術。J2EE項目就可以通過框架的設計和運用來達到控制軟件質量提高軟件的生產效率。2.2.1 框架出現的原因框架,在大型應用中還是相當有好處的。在多人蔘與,長時間的開發,業務邏輯複雜的情況下,使用框架可以很好的進行業務分層,代碼分層,可以實現同步開發,提高開發效率,縮短開發週期。現有的應用程序大致上有兩類性質不同的組件組成:一類與程序要處理的具體事務密切相關,把它們叫做業務組件:另一類是應用服務。不同的應用程序的業務組件肯定是不同的,但是有一些通用的服務只與程序相關而與業務無關。 可以把這些不同應用程序裏面的通用部分抽取出來,做成一個程序框架,具體的業務邏輯就可以在這個框架上實現。許多大型軟件企業由於長時間的項目積累,把一些優秀的,被實踐證明是高效的模塊提取出來,構建自己的框架。這樣可以大大節約開發成本,提高開發效率。2.2.2 框架的定義框架實現了更高級別的軟件複用形式---設計複用,那麼,具體來說框架是什麼和由什麼成分組成,還缺乏普遍認可的定義。關於框架有多種不同但相似的定義:(1)框架是能被程序員在特定的計算機問題中使用、擴展或者定製的一組普遍的,預先定製好的軟件構建模塊的集合。使用框架,開發者在開發應用項目時無需每次從頭開始。(2)框架是從對象集合構建的,框架的設計和代碼都可以被重用。(3)框架是構成一類特定軟件可複用設計的一組相互協作的類;是一組具體表達了抽象設計的類用於解決一族相關的問題;是一個能被開發者插入自己的代碼,並能提供大部分的通用功能的應用架構。歸納上述定義,框架是整個或部分系統的可重用的設計,表現爲一組抽象的構件及構件實例間交互的方法:或認爲,框架是可被應用開發者定製的應用骨架,前者是從應用方面而後者從目的方面給出的定義。具體來說,一個框架由一組協作類組成,闡明瞭整個設計、類間依賴及成員類的責任分步。這些類通常是抽象類,實現細節放在具體子類中,構成一個抽象設計,不同的子類構成對設計的不同實現。特定領域應用系統共有的設計因素由框架預先定義,應用開發人員只須關注與特定應用系統的特有部分,框架刻畫了其應用領域所共有的設計決策,所以說框架着重於設計複用。 2.2.3 框架的特點框架是一個可複用的軟件成分,它可以看作應用系統實現的一個半成品,它爲一類相似應用系統提供了共有結構的設計與實現,因此框架可以針對特定的應用系統漸進行擴展。具體地說,框架有以下主要特點:(1)抽象性。框架抽象了特定領域中一組相似地應用系統(或子系統)設計與實現地共有部分,並給出了這些部分地設計和實現。(2)可擴展。性由於框架抽象了一類相似應用的共同部分開發者可以擴展這些共同部分(如抽象類)實現特定的應用。(3)迭代性。框架開發過程本身具有迭代性。在使用框架的過程中,框架支持領域的共性被不斷髮掘,因此框架需要反覆的開發來完善其功能。(4)結構性。框架定義了特定領域中應用系統公共結構的設計和實現。開發者在這個共同的結構上擴展其功能實現具體的應用,因此一個好的框架具有明確清晰的層次結構,以便於複用和擴展。(5)可複用性。可複用性是框架的最根本的特點,也是框架具有上述特點的最終目的,框架是一種大粒度的可複用資源,是從設計到實現層次的複用,但它也必然地包括小粒度的複用---類的複用(如對抽象類的繼承)。2.3 輕量級框架的發展目前輕量級框架發展非常的繁榮,同一產品類別沒有一到兩週都有新的框架推出,比較成功的應用框架有這幾個方面:在Web層面主要是採的MVC體系結構的Struts2框架,在業務層非常成功的主要有Spring和Hibernate框架,這兩個框架聯合起來的作用相當於在J2EE體系結構中的EJB組件發揮的作用。下面簡單對Struts2、Spring和Hibernate做一介紹:Struts2是採用Java Servlet/JSP技術,開發Web應用程序的開放源碼的框架,採用Struts2能開發出基於MVC(Model-View-Controller)設計模式的應用構架,Struts2有一組相互協作的類、Servlet以及JspTaglib組成。Struts2有如下的主要功能:(1)包含一個controller servlet,能將用戶的請求發送到相應的Action對象。(2)JSP自由tag庫,並且在controller servlet中提供關聯支持,幫助開發員創建交互式表單應用。(3)提供了一系列實用對象:XML處理通過Java Reflection APIs自動處理JavaBeans屬性、國際化的提示和消息。Spring是一個以反向控制(IoC,Inversion of Control)原則爲基礎的輕量級框架。IoC也可以叫依賴注入(DI,Dependency Injection),即組件之間的依賴關係由容器在運行期決定,由容器動態將某種依賴關係注入到組件,這是Spring核心,其設計目標是提供一種無侵入式的高擴展性構架。即無需代碼中涉及Spring專有的類,即可將其納入Spring容器進行管理。作爲對比,EJB則是一種高度侵入性的框架規範,它制定了衆多的接口和編碼規範,要求實現者必須遵從。侵入性的後果就是,一旦系統基於侵入性框架設計開發,那麼之後任何脫離這個框架的企圖都將付出極大的代價。爲了避免這種情況,實現無侵入性的目標,Spring大量引入了Java的Reflection機制,通過動態調用的方式避免硬編碼方式的約束,並在此基礎上建立了其核心組件BeanFactory,以此作爲其依賴注入機制的實現基礎。Spring已成爲企業應用的一站式(即在一個服務點可以完成所有服務)選擇;同時Spring也是組件化的,允許使用它的部分組件而不需牽涉其他部分。可以使用 bean容器,在前臺展現層使用Struts,還可以只使用Hibernate集成部分或是JDBC抽象層。Spring是無侵入性的,根據實際使用的範圍,應用對框架的依賴幾乎沒有或是絕對最小化的。Spring把程序中所涉及到包含業務邏輯和Dao的Objects,例如事務管理控制(transaction management handler)、對象工廠(Object Factories)、服務組件(service objects),都通過XML來配置聯繫起來。iBatis是O/R Mapping框架的新貴,先已成爲Apache開源組織的子項目。iBatis框架以POJO與SQL語句之間的映射來完成持久層的工作。iBatis框架不像Hibernate自動生成SQL語句來運行,具體的SQL需要程序員編寫,然後通過映射配置文件,將SQL所需要的參數,以及返回的結果字段映射到指定的POJO。iBatis以SQL開發的工作量和數據庫移植上的讓步,爲系統設計提供了更大的自由空間。輕量級框架體系結構設計3.1 框架的設計分析在J2EE的應用開發中,軟件的可維護性和可擴展性是軟件設計者着重考慮的目標,它同時也是衡量J2EE應用系統設計的重要標準,要實現這個目標,就要降低軟件之間的耦合性,實現軟件功能之間的解耦。常用的結構設計是採用分層結構,分層是軟件設計中使用最多的方法,也是J2EE具有特色的一面。合理地使用分層技術可以將層次間的依賴性減到最少,還有利於標準化工作。本文設計的輕量級框架(命名爲TeaFramework框架)的設計同樣也是採用分層的結構,先看一下J2EE典型的三層結構,然後設計我們自已的三層結構,以及對於每一層的框架組件。J2EE典型的三層結構如圖3.1所示。

圖3.1  J2EE經典的三層結構 上圖是典型的J2EE三層應用框架,在表示層主要任務是負責頁面的顯示和Web頁面的流程控制,在中間層主要是實現業務邏輯部分,這部分對應的是由EJB技術來提供支撐的。最後是數據的持久化實現,主要通過DAO或者JDBC等來實現數據的持久化,一般把數據持久化在數據庫中。3.2框架的總體設計針對J2EE的三個層次:表現層,業務層,持久層的劃分,具體到本文設計的輕量級框架(TeaFramework框架)總體的設計,同樣是實現這三層結構,但與上面有所不同,以下是本文TeaFramework框架的各個模塊的層次結構。TeaFramework整體結構圖如圖3.2所示。

       圖3.2 TeaFramework整體結構圖    在TeaFramework框架的分層結構圖中給出了各個層次對應的框架組件,下面分別介紹這些組件的作用:(1)表現層。在TeaFramework框架中的MVC組件,主要是實現頁面的表示和MVC控制器的實現。頁面表示,只要是JSP頁面,這部分主要負責數據和信息的顯示,較少地涉及到業務邏輯;控制器只要是控制Web的請求處理流程。本文把表現層框架,命名爲TeaMVC框架,主要的配置文件爲tea-action.xml。(2)業務層。主要是基於IoC模式的容器部分,提供基礎組件和業務組件的依賴注入,從而真正的實現幫助組件之間的解耦工作。本文把業務層框架,命名爲TeaIoC框架,主要的配置文件爲tea.context.xml。(3)數據持久層。主要是實現對象到關係數據庫的映射關係,從而實現對象到關係數據庫的持久映射。本文把持久層框架,命名爲TeaORM框架,主要的配置文件爲tea.orm.xml。4 輕量級表現層框架設計與實現4.1 WEB應用系統傳統開發模式分析基於J2EE平臺的應用系統的傳統開發方式主要是使用JSP開發的兩種方式,即Model1(JSP+JavaBean模型)和Model2(JSP+Servlet+JavaBeans模型),這兩種模型的主要差別在於它們處理業務的流程不同。圖4.1所示的Model1中,JSP頁面響應客戶請求並將處理結果返回給客戶,所有的數據通過JavaBean來處理,JSP實現頁面的表現。

圖4.1 JSP Model1Model1部分實現了頁面表現和業務邏輯相分離。然而這種方式就要在JSP頁面使用大量的Java代碼,當需要處理的業務邏輯很複雜時,這種情況會變得非常糟糕。大量嵌入式代碼使整個頁面程序變得異常複雜對於前端界面設計的網頁開發人員來說,這將造成極大的開發難度。所以,Model1不能滿足大型應用的需求。圖4.2所示的Model2結合了JSP和Servlet技術,充分利用了JSP和Servlet兩種技術原有的優勢。這個模型使用JSP技術來表現頁面,使用Servlet完成大量的業務處理,使用JavaBean來存儲數據。Servlet用來處理請求,充當控制器的角色,並負責向客戶發送請求。它創建JSP需要的JavaBean對象,然後根據用戶請求的行爲,決定將具體的JSP頁面發送給客戶。

圖4.2 JSP Model2

從開發的觀點看,Model2具有更清晰的頁面表現和清楚的開發角色劃分,而且基本實現了MVC設計模式,分離了模型。控制和視圖,因此,Model2可以符合大多數WEB應用對體系結構方面的要求。從WEB應用的共性分析,WEB應用系統不僅具有類似MVC、模型二的體系結構方面的共性,還有一些共性的操作,如請求轉發、調用業務邏輯和選擇組件視圖等。即WEB應用系統需要一個除J2EE平臺提供的服務以外的基礎服務,而Model1或Model2只是一種概念模型,並沒有提供該基礎服務;同時,大型WEB應用在項目進度、項目投入等方面的要求也迫使開發者不能完全從零開發,即大型應用需要在既具有優良體系結構又提供一些共性的基礎服務的支撐上開發,因此,Model2仍然不能完全滿足大型WEB應用系統的需要。4.2 表現層MVC框架的設計爲了解決之前討論的WEB開發模型的缺陷,本文根據Struts2的思想和設計理念提出了通過輕量級的TeaMVC通用表現層框架來解決大多數通用的操作,進而提升開發效率及體系結構的完整性。TeaMVC框架正是在WEB應用層與J2EE平臺之間需要插入一個新的框架支撐層的需求下產生的。TeaMVC應用框架在基於J2EE的WEB應用系統中位於J2EE平臺上,提供WEB應用系統共同的操作,例如請求轉發,調用應用邏輯和選擇組件視圖。TeaMVC框架爲WEB應用系統提供了一些框架和接口,應用開發者只需繼承或實現這些類和接口,就可以完成具體應用的功能。TeaMVC框架的實現利用了Model2架構及MVC模式思想,同時也部分借鑑了層次模式的思想。其體系結構如圖4.3所示。TeaMVC框架有一下特徵:(1)TeaMVC框架提供了應用功能可編程的配置模塊。TeaMVC框架利用tea-action.xml配置文件,配置了系統的所有請求處理流程及對應的視圖。將系統的請求處理流程配置在文件中,可以有效降低控制、模型、視圖三者間的耦合,同時可以通過簡單配置修改完成系統功能的增加、刪除,從而提高了系統的可擴展性。(2)對於所有客戶端請求,TeaMVC框架利用一個核心Filter作爲框架的主控制器,如圖所示TeaFrameworkFilter。當主控制器接收到一個請求時,它將該請求委託給處理器Dispatcher處理,最終在ActionInvocation中去完成具體的控制器的調用。(3)TeaMVC框架整體是設計成線程安全的,對於每個客戶端請求,TeaMVC都會創建唯一一個控制器Action對象及執行調用器ActionInvocation,還有就是Action的上下文環境ActionContext,這從根本上隔離了TeaMVC對於不同請求的處理。(4)TeaMVC框架的核心是引入了Struts2中的攔截器模式,框架所有的功能都可以通過攔截器進行擴展的,開發人員也可以開發自己的攔截器進行業務邏輯的處理,這就給框架帶來很好的可擴展性。

圖4.3 TeaMVC體系結構圖4.3 TeaMVC框架的實現TeaMVC有兩條運行主線,即初始化主線和Http請求處理主線。這兩條運行主線無論從運行空間、運行特點以及所承擔的職責上來看都完全不同。從運行空間上講,兩者沒有任何交集,運行的觸發條件也完全不同;從運行特點上講,初始化主線在系統啓動時運行一次,Http請求處理主線則在系統啓動完畢後以監聽請求的方式運行;從所承擔的職責上講,初始化主線是爲整個TeaMVC建立起運行所必備的運行環境,而Http請求處理主線則是TeaMVC的核心功能。4.3.1 框架的初始化TeaMVC的兩大主線的入口程序是相同的,都是TeaFrameworkFilter,只不過是不同的方法的驅動了兩條不同的運行主線。本框架也正是基於Filter所實現的Servlet規範中的不同的方法的生命週期的不同,規劃了TeaMVC不同的運行主線。其中.init方法就是TeaMVC初始化主線的入口方法,其中相關代碼如下:public class TeaFrameworkFilter implements Filter {private PrepareOperations prepare;private ExecuteOperations execute;public void init(FilterConfig filtereConfig) throws ServletException {InitOperations init = new InitOperations();String actionConfig = filtereConfig.getInitParameter("actionConfig");     /** 讀取並解析action.xml,把信息存入ConfigurationManager中  */     Dispatcher dispatcher = init.initDispatcher(actionConfig);     this.prepare = new PrepareOperations(filtereConfig.getServletContext(),dispatcher);     this.execute=new ExecuteOperations(filtereConfig.getServletContext(),dispatcher);    }       ……..// 這裏省略了許多其他代碼}TeaMVC的初始化工作主要針對三個元素(Dispatcher、PrepareOperations、ExecuteOperations)展開的,而這三個元素也成爲之後Http請求處理主線的執行依據。而入口程序TeaFrameworkFilter的init方法的核心,是通過init.initDispatcher方法調用返回核心分發器Dispatcher的對象。該方法的相關代碼如下:public Dispatcher initDispatcher(String actionConfig) {     Dispatcher dispatcher = new Dispatcher();dispatcher.initDispatcher(actionConfig);return dispatcher;}public void initDispatcher(String actionConfig){this.configurationManager = new ConfigurationManager();/** 開始載入與解析TeaMVC的核心配置文件 */this.configurationManager.configInit(actionConfig);}TeaMVC初始化主要是對核心配置文件tea-action.xml的加載和解析,這也是進行請求處理的依據。本框架是通過XML開源解析工具Dom4j來解析XML配置文件的,先配置如下配置文件:<?xml version="1.0" encoding="UTF-8"?>  <tea-web><!--系統開發常量配置,Action是否由IoC容器來控制--><config-const name="ioc" value="true"/>    <!-- 配置可編程控制器模塊 --><controller name="admin" namespace="/admin" >    <interceptors>     <interceptor name="my" class="test.interceptor.MyInterceptor"/>     <interceptor-stack name="checkLogin">        <interceptor-ref name="my"/>       <interceptor-ref name="defaultStack"/>     </interceptor-stack>        </interceptors>        <!-- 配置默認攔截器棧 -->        <default-interceptor-ref name="checkLogin"/>        <!-- 配置全局的result  -->    <global-results>    <result name="error" url="/error.jsp"/>    </global-results>           <action name="login" class="userAction" method="login"><result name="success" url="/success.jsp"/>    <!-- 引用自定義的攔截器 -->    <!-- <interceptor-ref name="checkLogin"/> -->    </action></controller></tea-web>由以上配置文件可以清晰地看到TeaMVC的配置文件的主體配置。配置可編程的模塊controller,即一定意義上的package;配置攔截器模塊interceptor和控制器模塊action;配置結果映射模塊result,映射到TeaMVC中的類名分別爲ControllerMapping,ActionMapping,InterceptorMapping,ResultMapping這幾個映射類。這些配置信息類最終會被全部存儲到ConfigurationManager中,供框架處理請求時獲取對應的配置信息。爲了更形象的說明TeaMVC的初始化過程,通過如圖4.4的順序圖來說明。

圖4.4  TeaMVC初始化順序圖4.3.2 入口過濾器的實現之前已經介紹過TeaMVC的入口程序被設計成一個TeaFrameworkFilter的過濾器,TeaFrameworkFilter的初始化init方法前面已經重點介紹了,這部分所關注的是Filter中真正處理Http請求的doFilter方法,TeaFrameworkFilter的完整源碼如下:public class TeaFrameworkFilter implements Filter {private PrepareOperations prepare;   private ExecuteOperations execute;/** 執行初始化工作 */public void init(FilterConfig filtereConfig) throws ServletException {InitOperations init = new InitOperations();       /** 從web.xml中得到MVC核心配置文件 */String actionConfig = filtereConfig.getInitParameter("actionConfig");/** 讀取並解析action.xml,把信息存入ConfigurationManager中 */Dispatcher dispatcher = init.initDispatcher(actionConfig);this.prepare=newPrepareOperations(filtereConfig.getServletContext(),dispatcher);this.execute=newExecuteOperations(filtereConfig.getServletContext(),dispatcher);}   /** 所有請求的入口 */public void doFilter(ServletRequest req, ServletResponse res,         FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)req;HttpServletResponse response = (HttpServletResponse)res;try { /** 創建ActionContext上下文環境  */   this.prepare.createActionContext(request, response);   /** 根據Http請求查找所對應的ActionMapping */ActionMappingactionMapping=this.prepare.findActionMapping(request);    if (actionMapping == null) {chain.doFilter(request, response);} else if (actionMapping.getName() == null) {Forward.forWard(request, response, Forward.ERROR_PAGE_404);} else {/** 處理請求 */this.execute.executeAction(request, response, actionMapping);}} finally {/**清理此請求的環境 */prepare.cleanupRequest(request);}}}doFilter方法是整個TeaMVC的第二條主線入口。在這個方法中,許多在第一條主線中被初始化的對象開始發揮作用,主要包括:(1)Dispatcher ---核心分發器,執行TeaMVC處理邏輯的實際場所。(2)PrepareOperations ---Http預處理類,對所有的Http請求進行預處理。(3)ExecuteOperations ---Http處理執行類,執行Http請求的場所。在進行URL處理時,doFilter方法會根據職責的不同,有條不紊地將URL處理過程分配給PrepareOperations和ExecuteOperations來分別完成。不過在這裏PrepareOperations和ExecuteOperations實際上只是被設計成一層薄薄的代理層,實際在其中起決定性作用的還是核心分發器Dispatcher。爲了解耦及職責的清晰性,本框架把請求處理的兩個階段分別設計了以上的PrepareOperations和ExecuteOperations兩個類,前一個主要負責TeaMVC的初始化工作和每個Http請求的預處理工作,而後一個則負責Http請求的實際邏輯處理。4.3.3 數據流體系的實現TeaMVC的數據流體系的載體被設計成ActionContext類(Action執行的環境),主要有兩個職責:數據存儲和數據共享。具體的實現如下:public class ActionContext { /**本地線程變量 */staticThreadLocal<ActionContext>actionContext=new ThreadLocal<ActionContext>();public static final String REQUEST = "com.tea.web.ActionContext.requestMap";public static final String SESSION = "com.tea.web.ActionContext.sessionMap";publicstaticfinalString APPLICATION = "com.tea.web.ActionContext.applicationMap";Map<String, Object> context;…… // 這裏省略了許多其他代碼}ActionContext的一大職責是數據存儲,從以上可以看到ActionContext內部有一個Map類型的context,這個Map就是ActionContext 真正的數據存儲空間,ActionContext將所有的數據對象以鍵值對存儲於context之中。而與此同時,ActionContext也提供了一些方便訪問Web元素的接口方法,在TeaMVC中又設置了一層對Web元素的代理,用Map類型的RequestMap來封裝HttpServletRequest,用Map類型的SessionMap來封裝HttpSession,用Map類型的ApplicationMap來封裝ServletContext。之所以這樣處理,是爲了減少用戶程序與Web元素之間的耦合性,用業務層的角度去處理數據,從而減少與Web元素的耦合。此外,作爲數據環境,ActionContext在設計上也考慮到了與Web容器對象進行交互的可能性。因此,爲了提供一個完整的與Web容器打交道的方案,TeaMVC在ActionContext的基礎上擴展了一個ServletActionContext的子類,並在其中封裝了與原生的Web元素打交道的接口方法,所以我們可以在TeaMVC應用中,通過ServletActionContext在任何編程層次被隨時調用,通過ServletActionContext所暴露的接口來完成對原生Web元素HttpServletRequest、HttpServletResponse的訪問。ActionContext的另一大職責是數據共享,在設計TeaMVC考慮到Web請求的特點,對於不同的Web請求,應該對於每個Http請求都有一個唯一的數據環境,來爲控制層程序的請求處理準備上下文數據,如果只設計一個全局的數據接口,則會出現線程安全,即對於所有請求都共享同一個數據環境,這樣請求之間就會互相產生影響。爲了解決線程安全問題,TeaMVC引入了ThreadLocal線程局部變量來解決線程安全問題,從以上實現可以看出,ActionContext內部封裝了一個靜態的ThreadLocal對象,而這一靜態的ThreadLocal對象所操作和存儲的對象,又是ActionContext對象本身,從而保證對於每一個請求都唯一對應一個上下文環境ActionContext對象,確保了請求的線程安全性。4.3.4 控制流體系的實現    TeaMVC的控制流體系被設計成如圖4.5所示。從圖中可以看到構成控制流的元素主要有五個:ActionProxy,ActionInvocation,Interceptor,Action,Result。TeaMVC把一個請求處理的控制流細化成了若干個以上組件,TeaMVC的控制流體系由於引進了攔截器(Interceptor),即應用了典型的AOP(面向切面編程)的設計思想。

圖4.5 TeaMVC控制流體系圖下面結合上圖詳細介紹TeaMVC控制流體系中的各種組件。(1)ActionProxy---執行窗口ActionProxy是整合TeaMVC框架的執行入口,ActionProxy的存在相當於定義了一個事件處理流程的執行範圍,主要作用就在於對外屏蔽整個控制流核心元素的執行過程,對內則爲Action、Interceptor、Result等事件處理節點對象提供一個無干擾的執行環境。還有值的注意的是對於每個請求都只對應一個ActionProxy對象。(2)ActionInvocation---核心調度器     ActionInvocation是TeaMVC處理請求的核心調度器,也是TeaMVC實現的重點。ActionInvocation是組織起Action、Interceptor、Result等事件處理節點對象執行次序的核心調度器。ActionInvocation被封裝在ActionProxy的內部,它的執行調度過程,實際上控制器整個TeaMVC的請求處理體系的執行命脈。ActionInvocation的核心調度功能是通過invoke方法完成的,具體的實現如下:@Overridepublic String invoke() throws Exception {if (this.current < this.interceptorSize) {InterceptorMapping  interceptorMapping =  interceptorList.get(this.current++);   /** 執行Interceptor的回調 */this.resultCode = interceptorMapping.getInterceptor().intercept(this);}else { /** 調用Action的方法,得到resultCode */invokeAction();}/** Result是否已經執行。第一次執行只執行一次,resultCode可能來自Action執行返回結果,也可能來自攔截器返回的結果 */if(!executed){ /** 根據resultCode創建相應的Result對象並執行 */executeResult();executed = true;}return this.resultCode;}以上ActionInvocation的invoke方法結合攔截器和遞歸調用,是TeaMVC實現AOP的具體體現。這個invoke方法應用了典型的攔截器模式,實現了攔截器和控制器執行順序的解耦,深刻體現了TeaMVC框架的設計思想。該方法具體的意思如下:如果執行棧中還有攔截器,那麼執行攔截器;否則,執行Action,緊接着執行Result。(3)Interceptor---攔截器      Interceptor本身是AOP的概念,表示對程序的某個邏輯執行點進行攔截,從而能夠在這個邏輯執行點之前、之後或者環繞着這個邏輯執行點進行邏輯擴展。在本文的TeaMVC框架中,Interceptor的攔截對象是核心控制器Action對象,在Action的周圍定義了一個環繞的邏輯擴展層次,其被設計的主要目的就在於能夠在覈心控制器Action的執行之前、之後進行自定義的邏輯行爲擴展。下面通過一個具體的示例攔截器AroundInterceptor來說明TeaMVC對攔截器的設計,實現代碼如下:Public class AroundInterceptor implement Interceptor {Public String intercept(ActionInvocation invocation){String result = null;     // 在整個執行棧執行完畢之前的邏輯擴展    before(invocation);// 驅動執行棧的進一步執行invocation.invoke();// 在整個執行棧執行完畢之後的邏輯擴展after(invocation,result);return result;}}在攔截器的實現中,又對ActionInvocation的invoke方法進行了遞歸調用。這樣遞歸調用的嵌套執行使得以遞歸調用邏輯爲中心的代碼段(invocation.invoke的調用)成爲邏輯上的分水嶺,從而徹底改變了原本正常的執行邏輯。一個有序鏈表通過遞歸調用變成了一個堆棧執行過程,將一段有序執行代碼變成了兩段執行順序完全相反的代碼過程,從而巧妙實現了AOP。(4)Action---核心控制器     Action是TeaMVC中所定義的一個核心控制器接口,本框架的設計的初衷就是爲了設計一套輕量級的Web框架,而可以讓程序員遠離Servlet方面編程,Action作爲代替Servlet作爲上層控制器,完全能勝任作爲控制器的需求。並且,在TeaMVC中一大設計亮點,就是通過內置攔截器(ParametersInterceptor)把請求參數解析出來通過第三方表達式引擎OGNL把請求參數設置爲Action的屬性,這樣,Action的屬性就成爲了每次請求的數據來源,方便了程序員集中精力去處理具體的業務邏輯,這也體現了TeaMVC抽取通用程序的框架思想。(5)Result—執行結果     Result是TeaMVC中定義用來對核心控制器類Action執行完畢之後的響應處理動作。在TeaMVC中之所以獨立定義一層Result,設計的組要目的在於這樣使得核心控制器能夠更加關注核心業務流程的處理,而將程序的跳轉控制邏輯交給Result來完成,具體的實現類有ServletDispatcherResult和ServletRedirectResult。4.3.5 一次完整請求處理的實現下面根據TeaMVC框架對客戶端請求的一個完成處理過程來說明TeaMVC處理一個完整請求的實現機制。具體的實現如圖4.6及4.7所示。一個請求在TeaMVC框架中的處理大概分爲以下幾個步驟:(1)客戶端初始化一個指向Servlet容器(例如Tomcat)的請求。(2)這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做CharactorEncodingFilter的可選過濾器)。(3)接着核心過濾器TeaFrameworkwork被調用,TeaFrameworkwork去配置文件讀取配置信息來決定這個請是否需要調用某個Action。(4)如果決定需要調用某個Action,TeaFrameworkwork會先得到Action對應的映射ActionMapping,再把請求的處理交給Dispatcher。

圖4.6 TeaMVC一次完整的請求處理(一)

圖4.7 TeaMVC一次完整的請求處理(二)

(5)Dispacther被設計成一個轉發類,其中的serviceAction是執行請求的入口,會通過ActionProxyFactory創建一個ActionProxy和ActionInvocation對象。然後把控制權交給ActionProxy代理類。(6)ActionInvocation實例使用命名模式來調用,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用。(7)一旦Action執行完畢,ActionInvocation負責根據tea-action.xml中的配置找到對應的返回結果。返回結果通常是一個需要被表示的JSP。(8)最終通過Result組件請求轉發到對應的視圖進行對此次請求的響應。4.4 MVC框架所涉及的設計模式4.4.1 ThreadLocal模式ThreadLocal模式嚴格意義上來說並不能說是一種設計模式,因爲它只是用來解決多線程程序中數據共享問題的一個方案,正因爲如此,TeaMVC框架也引進了這種解決方案,通過ThreadLocal來保證每個請求(線程)擁有線程私有的Action的上下文環境ActionContext對象。ThreadLocal執行示意圖如圖4.8所示。

圖4.8  Thread執行示意圖由於ThreadLocal所操作的是維持與整個Thread生命週期的副本(Thread LocalMap),所以無論在J2EE程序的哪個層次(表現層、業務層或持久層),只要在一個線程Thread的生命週期之內,存儲於ThreadLocalMap中的對象都是線程安全的(因爲ThreadLocalMap本身僅僅隸屬於當前線程Thread,是線程Thread內部的一個數據成員)。而這一點,正是我們用來解決多線程環境中的數據共享問題的核心技術。ThreadLocal的這一特性也使其能夠廣泛應用於J2EE開發中的許多業務場景。4.4.2 攔截器模式Web層請求處理機制可以接收許多不同類型的請求,這些請求分別需要不同類型的處理。有些請求只是簡單的轉發給合適的處理器組件,而有的請求在進一步處理之前必須進行修改、轉換等預處理。攔截器設計模式正是爲了解決請求的預處理和響應的後處理的問題。其類圖如圖4.9所示。

圖4.9 攔截器模式結構圖FilterChainCreator負責過濾器的處理,在TeaMVC中對應於着ActionProxy。它會用合適的過濾器,按照正確的順序來創建過濾器鏈,並且初始化處理過程。FilterChain是獨立的過濾器的有序集合,在TeaMVC中對於着ActionInvocation。Filter是單獨的過濾器,在TeaMVC中對於着攔截器Interceptor。FilterChain會協調這些過濾器的處理。Target是所請的目標資源,在TeaMVC中是Action對象。4.4.3 策略模式策略模式的意圖是定義一系列的算法,把它們一個個封裝起來,並且使它們可以相互替換,最終算法可以獨立於使用它的客戶而變化。策略模式的核心是對算法的封裝,其最終目的是把使用算法的環境和算法的實現進行解耦。在這種情況下,策略模式中的算法的實現都是“可插拔式”的。其類圖如圖4.10所示。

圖4.10 策略模式結構圖

4.5 MVC框架配置及使用說明要應用TeaMVC框架,首先必須要在項目的web.xml中指定TeaMVC的入口程序,如下所示:<!-- 配置Tea MVC框架的入口Filter --><filter><filter-name>actionFilter</filter-name><filter-class>org.teaframework.web.filter.TeaFrameworkFilter</filter-class>     <init-param><param-name>actionConfig</param-name><param-value>tea-action.xml</param-value></init-param></filter><filter-mapping>   <filter-name>actionFilter</filter-name>   <url-pattern>/*</url-pattern></filter-mapping>配置完入口程序,就可以根據需求配置TeaMVC的核心配置文件tea-action.xml了,示例配置如下:<?xml version="1.0" encoding="UTF-8"?>  <tea-web><!-- 配置可編程控制器模塊 --><controller name="admin" namespace="/admin" > <interceptors><interceptor name="my" class="test.interceptor.MyInterceptor"/><interceptor-stack name="checkLogin"><interceptor-ref name="my"/><interceptor-ref name="defaultStack"/></interceptor-stack></interceptors><!-- 配置默認攔截器棧 --><default-interceptor-ref name="checkLogin"/><!-- 配置全局的result  --><global-results>   <result name="error" url="/error.jsp"/></global-results><!-- 配置核心控制器Action --><action name="login" class="org.tea.UserAction" method="login"><result name="success" url="/success.jsp"/>   <!-- 可以引用自定義的攔截器 -->   <!-- <interceptor-ref name="checkLogin"/> --></action></controller></tea-web>   配置完核心配置文件就可以編寫核心控制器Action了,如下:public class UserAction extends ActionSupport{ // 請求數據來源   private User user = new User();   public User getUser() {   return user;   }   public void setUser(User user) {   this.user = user;   }   public String login() {   if (user.getUserName().equals("Tea")) {      return SUCCESS;   } else {      return ERROR;   }    }}可以通過瀏覽器輸入http://localhost:8080/admin/login.action?user.username=tea進行訪問了。

5 輕量級業務層框架設計與實現5.1 IoC模式的分析5.1.1 IoC模式的產生在設計模式中,提倡一種思維編程方式:接口驅動。接口驅動有很多好處,可以提供不同靈活的子類實現,增加代碼穩定和健壯性等。面向抽象、面向接口編程是面向對象的核心。

圖5.1 面向接口編程示意圖上面的組件A依賴組件B在接口編程中,分爲兩部分,一是接口IB部分,其提供功能的聲明,二是接口的一個實現部分B1。下面都假設IB接口,B1是IB接口的一個實現。那麼接口的實現形如:IB b = new B1();通過接口應用就如同空的模型套,在必要時,需要向模型套注射石膏,這樣才能成爲一個模型實體,因此,人爲控制接口實現形成“注入”,這也是依賴注入(DI)概念的體現。上述IB接口實現語句表明當前是在調用被調用者A,由於被調用者B1的實現名稱寫入了調用者A的代碼中,這產生了一個接口實現的問題:彼此聯繫,調用者和被調用者有緊密聯繫,在UML中是用依賴 Dependency 表示。這種緊密聯繫的依賴與關注點分離(Separation of Concerns,SoC)思維是相違背的。它們之間必需解耦,IoC模式由此產生,IoC框架就是將依賴先剝離,然後在適當時候由容器負責產生具體的實例再注入到調用者中。5.1.2 IoC模式與工廠模式IoC模式與工廠模式有一定的相似之處,即它們都可以剝離依賴關係。可以實現根據配置文件動態實現實例注入。上例中,A作爲調用者,IB是被調用者,在A代碼中存在對IB的調用,僞碼如下:public class A{ private IB comp;... } 工廠方法:上例A類中增加如下僞代碼如下:private final static MyFactory myFactory = MyFactory.getInstance();public A(){ this. comp = myFactory.createInstanceOfIB();} 用工廠方法,也可以通過定義xml配置文件定義IB的子類的實現,通過createInstanceOfB()生成B1的具體實例。IoC:運用IoC框架實現:上例則是在A中增加public void setComp(IB comp){ this.comp = comp; }在配置文件中添加相應的依賴:<bean id=“a” class=“A”><property name=“comp” ref=“comp”/></bean><bean id=“comp” class=”BI”/>客戶端調增加用爲:public class client{ public static void main( String[] args ) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory ();A a = (A) beanFactory.getBean(“a”);a.someMethod();}}可以看出,IoC框架的特點是A類如同通常POJO類,沒有任何在A類代碼中嵌入任何工廠模式等代碼,但在配置文件中配置了依賴,實現了依賴可配置化。工廠模式與IoC框架的區別:IoC框架是工廠模式的的昇華,使用IoC徹底解耦了A和BI之間的聯繫,使用IoC,在A類代碼中將不需要嵌入任何工廠模式等的代碼,而工廠模式其實還是與BI有些間接的聯繫,IoC框架只是將這種聯繫轉移到配置文件進行依賴,由於IoC解放自由了調用者類,而且可以向調用者類實現注入所依賴的接口的各種實現方案。5.1.3 IoC模式產生的重要意義從上面的分析可以看出IoC組件(框架)的意思在於以下三個方面:(1)顛倒了“使用對象之前必須創建” 的基本Java語言定律因爲有了IoC組件,對象的調用可以直接從IoC容器中取得,而不是在類中負責調用對象的生命週期。把類的創建和初始化工作統一交給IoC容器來處理,由IoC容器來統一負責類的創建處理,是IoC容器發展的重要驅動力,從這一思想的出發點也正是分離關注點(Separation of Concerns,SOC)的思想的體現。(2)組件間的依賴關係減少極大改善了代碼的可重用性通過IoC容器可以在運行期爲組件配置所需資源,而無需在編寫組件代碼時就加以指定,從而在相當程度上降低了組件之間的耦合。將依賴關係從編碼中脫離出來,從而大大降低了組件之間的耦合,實現了組件真正意義上的即插即用。(3)真正的面向接口編程使面向接口編程更加優雅,更加自然通過接口編程實現了程序的動態綁定,從而降低了組件的依賴關係,IoC容器爲面向接口編程提供了一個更加自然的平臺。程序員會自然而然傾向於使用接口來定義不同層次之間的關聯關係,這種自發的傾向性,來自於IoC組件所提供的簡單舒適的依賴注入實現。這使得接口的定義和使用不再像傳統編碼過程中那麼繁瑣(傳統編碼過程中,引入一個接口往往也意味着同時要引入一個Factory類,也許還有一個額外的配置文件及其讀寫代碼)。5.2 業務層IoC框架的設計5.2.1 IoC容器的總體設計TeaIoC容器主要包括以下幾個部分:註冊模塊、解析模塊、自動裝配模塊、命週期管理模塊和緩存管理模塊。其整體架構和其在整個系統中的地位如圖5.2所示:圖5.2 IoC容器的整體結構圖(1)解析模塊組件信息在容器中註冊以後並不能直接使用。由於配置文件的XML設計通常是遞歸的,所以配置文件通常會比較複雜,這樣在容器中所註冊的信息也會比較複雜,並且通常配置的組件信息支持繼承結構,這樣就需要合併組件的配置信息。解析模塊就是把這些複雜信息解析爲能夠直接爲管理組件對象的生命週期所直接使用的參數。(2)註冊模塊容器根據配置文件的XML設計讀取並解析配置文件,把讀取的信息保存在適當的數據結構中,然後在容器中爲每個組件註冊信息爲以後容器管理這些組件做準備。(3)自動裝配模塊IoC容器能自動裝配組件與組件之間的依賴關係,也就是不需顯示指定所依賴的組件,由IoC容器檢查配置文件的內容,爲主調組件注入依賴關係。自動裝配可以具體指定到每個組件,可讓某些組件使用自動裝配而某些組件不使用,自動裝配減少了配置文件的工作量。(4)生命週期管理模塊組件對象實例處於容器中,可以通過不同的實例化策略實例化它,在創建後需要執行一些初始化動作,這就是對象的生命週期管理。IoC容器負責管理組件對象的生命週期,如對象的創建、初始化和銷燬等。對象的生命週期就是從無到有再到消亡不斷變化的過程,容器必須對這個過程進行管理。(5)緩存管理模塊TeaIoC容器採用了緩存技術,以提高容器對組件管理的性能。把帶有唯一標示的組件實例放置於緩存中,緩存的作用是保存一些組件實例,減少組件重新裝載的時間。當程序需要訪問某一個組件的實例時,容器先檢查這個實例在不在緩存中,如果在緩存中,則緩存命中,返回實例,可以直接使用,如果不在的話,就需要將組件裝載到容器中去,然後保存在緩存中。在IoC容器中,緩存用一個ConcurrentHashMap表示,組件以其在容器中的唯一ID作爲鍵值存儲到ConcurrentHashMap中。(6)配置文件容器需要提供統一的方式配置運行在其中的對象,配置方式應該簡單適用,並且配置代碼應該脫離程序代碼,這樣在改變配置時就不需要重新編譯文件。配置文件是通XML文件表示的。5.2.2 IoC框架的關鍵技術(1)XML解析技術      XML在不同語言裏解析方式都是一樣的,用Java解析XML文檔,只不過實現的語法不同而已,Java中最常用的有兩種方法:使用基於事件的XML簡單API(Simple API for XML)稱爲SAX和基於樹和節點的文檔對象模型(Document Object Module)稱爲DOM。SAX是基於事件流的解析,DOM是基於XML文檔樹結構的解析。另外還有幾種基於以上兩種解析技術的開源解析包:Dom4j和Jdom解析開發包。IoC框架採用原生的DOM解析技術來解析XML文件的。(2)Java虛擬機類加載機制 Java中之所以可以在運行時動態地加載指定的類,是由於Java虛擬機的類加載器技術的實現,類被類加載器加載到虛擬機內存中,整個生命週期包括了:加載、驗證、準備、解析、初始化、使用和卸載七個階段。具體我們可以通過Class.forName()和classLoader.loadClass()來實現動態地加載類。(3)Java反射機制Java反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性。Java提供的反射機制的類主要有java.lang.Class和java.lang.reflect包中的Constructor、Method等類。IoC框架通過反射注入的實現思路如下:實現在運行期根據需要動態的實現對象的注入功能,並且實現對注入對象的生命週期的管理等相關任務。其過程是首先容器分析組件間的依賴關係,然後利用Java的反射機制,要動態的生成調用者和被調用者類的實例,必須先得到該類的無參構造器Constructor然後通過Constructor.newInstance將產生相應的實例,取得實例後,將把被調用者的實例注入到調用者的實例中。set注入需要先生成調用者實例,然後取得調用者實例的相應的Setter方法的Method類型的writeMethod對象,然後調用writeMethod.invoke(obj,value)將新生成的被調用者實例注入進去。其中value中包括被調用者實例的參數對象。(4)JavaBean構件 Java.beans包提供了大量方便操作JavaBean的API,其基礎結構仍爲反射機制。在java.beans包中比較重要的有Introspector類,BeanInfo接口,PropertyDescriptor類和PropertyEditor接口等。(5)設計模式 如何同時提高一個軟件系統的可維護性(Maintainability)和可複用性(Reusability)是面向對象的設計要解決的核心問題,通過學習和應用設計模式,可以更加深入地理解面向對象的設計理念,從而幫助設計師改善自己的系統。在IoC框架中用到的主要有開放封閉原則、模板方法模式、代理模式等。5.2.3 IoC框架在TeaFramework中的作用TeaFramework框架的設計中, IoC框架起到了兩方面的作用:(1)對整體的TeaFramework框架起到一個基礎組件支撐的作用。在TeaFramework框架中有很多的組件需要管理,要實現組件的可管理化和可配置化,實現整個系統的可擴展性和可伸縮性。需要這樣的一個IoC基礎框架來提供支撐。(2)實現對應用業務組件的上層支撐。就是用戶的業務組件可以通過TeaFramework的IoC框架功能實現可管理可配置,實現對用戶業務組件的解耦.這樣大大的提高了用戶的業務組件的可擴展性和靈活性。TeaIoC框架也是一個獨立的框架,可以讓用戶單獨使用,對用戶的業務組件起到支撐作用。5.3 業務層IoC框架的實現IoC框架的實現可以分成兩個步驟,第一個步驟是IoC容器的初始化,包括BeanDefinition的載入與解析、BeanDefinition在IoC容器中的註冊,第二個步驟就是Bean的實例化及依賴注入。IoC容器的初始化和依賴注入是兩個獨立的過程,依賴注入是在應用第一次通過getBean向容器索取Bean的時候,這裏有一個實現的細節,在使用IoC容器時有一個預實例化的配置,通過這個預實例化的配置(具體來說,可以通過爲Bean定義信息中的lazy-init屬性),開發者可以對容器初始化過程作一個微小的控制,從而改變這個被設置了lazy-init屬性的Bean 的依賴注入過程。下面就通過IoC容器初始化及依賴注入的實現細節詳細論述IoC框架的實現。5.3.1 BeanDefinition的載入與解析TeaIoC通過定義BeanDefinition來管理基於Tea框架的應用中的各種對象以及它們之間的相互依賴關係。BeanDefinition抽象了我們對Bean的定義,是讓IoC容器起作用的主要數據結構。對於IoC容器來說,BeanDefinition就是依賴注入模式中管理的對象依賴關係的數據抽象,也是容器實現依賴注入功能的核心數據結構,控制反轉功能都是圍繞對這個BeanDefinition的處理來完成的。對於IoC容器來說,BeanDefinition的載入與解析過程,相當於把定義的BeanDefinition在IoC容器中轉化成一個TeaIoC內部表示的數據結構的過程。TeaIoC容器對Bean的管理和依賴注入功能的實現都是通過對其持有的BeanDefinition進行各種相關操作來完成的。這些BeanDefinition數據在TeaIoC中的通過一個ConcurrentHashMap來保持和維護。BeanDefinition的載入與解析分成兩部分,首先通過調用XML的解析器得到Document對象,但這些Document對象並沒有按照TeaIoC的Bean規則進行解析。在完成通用的XML解析之後,纔是按照TeaIoC的Bean規則進行解析的地方。具體的載入與解析的序列圖如圖5.3所示。XML定義的BeanDefinition就被整個載入到了IoC容器中,並在容器中建立起了數據映射,在IoC容器中的建立起了對應的數據結構,或者說可以看成是POJO對象在IoC容器的抽象,這些數據結構可以讓IoC容器執行索引、查詢、操作。現在,已經實現了BeanDefinition的載入與解析,但是這時候的容器還沒有完全起作用,要完全起作用,好需要完成數據向IoC容器的註冊。

圖5.3 BeanDefinition的載入與解析的序列圖5.3.2 BeanDefinition在IoC容器中的註冊之前已經實現了BeanDefinition的載入與解析,在這些功能完成後,用戶定義的BeanDefinition信息已經在IoC容器內建立起了自己的數據結構以及相應的數據表示,但此時這些數據還不能供IoC容器直接使用,需要在IoC容器中對這些BeanDefinition數據進行註冊,這個註冊的設計,爲TeaIoC容器提供了更友好的使用方式,在覈心容器類DefaultListableBeanFactory中,是通過一個ConcurrentHashMap來持有載入的BeanDefinition的,這個ConcurrentHashMap的定義如下:/** 通過Map來持有BeanDefinition */Private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();將解析得到的BeanDefinition向IoC容器中的beanDefinitionMap註冊的過程是在載入與解析BeanDefinition完成後進行的,註冊的調用過程如圖5.4所示。

圖5.4 BeanDefinition在IoC容器中註冊的序列圖在具體實現上,核心容器類DefaultListableBeanFactory實現了BeanDefinition Registry接口,這個接口完成BeanDefinition向IoC容器的註冊。這個註冊就是把BeanDefinition設置到beanDefinitionMap中去。完成了BeanDefinition的註冊,就完成了IoC容器的初始化過程。此時,在使用的IoC容器DefaultListableBeanFactory中已經建立了整個Bean的配置信息,而且這些BeanDefinition已經可以被容器使用了,它們都再beanDefinitionMap裏被檢索和使用。容器的作用就是對這些信息進行處理和維護。這些信息是容器建立依賴注入的基礎,有了這些基礎數據,就可以實現IoC容器的依賴注入功能了。5.3.3 Bean的實例化及依賴注入的實現到現在爲止,IoC容器已經載入了用戶定義的Bean信息,就可以實現依賴注入的功能了。首先,依賴注入的過程是用戶第一次向IoC容器索要Bean時觸發的,當然也有例外,我們可以也可以在BeanDefinition信息中通過控制lazy-init屬性來讓容器完成對Bean的預實例化,這個預實例化也是一個依賴注入的過程,但它是在初始化的過程中完成的。當用戶第一次向IoC容器索要B ean的入口在AbstractBean Factory,具體實現如下:/**  getBean方法觸發依賴注入 */public Object getBean(String name) {return doGetBean(name,null);}protected <T> T doGetBean(final String beanName,final Class<T> requiredType){Object sharedInstance = getSingleton(beanName);Object bean = null;    if (sharedInstance != null){bean = sharedInstance;}else{ /**  根據Bean的名字取得BeanDefinition*/final BeanDefinition mbd = getBeanDefinition(beanName);     /** 這裏創建Singleton類型的Bean對象 */    if (mbd.isSingleton()) {    sharedInstance = getSingleton(beanName, new ObjectFactory(){    public Object getObject(){return createBean(beanName, mbd);    }});bean = sharedInstance;}     /** 這裏創建Prototype類型的Bean對象 */else if (mbd.isPrototype()) {    Object prototypeInstance = null;prototypeInstance = createBean(beanName, mbd);bean = prototypeInstance;}}return (T)bean;}這個就是依賴注入的入口,在這裏觸發了依賴注入,而依賴注入的發生是在容器中的BeanDefinition數據已經建立好的前提下進行的。重點來說,getBean是依賴注入的重點,之後會調用AbstractAutoCapableBeanFactory的createBean方法,createBean的實現不但生成了需要的Bean對象,還對Bean初始化進行了處理,比如實現了在BeanDefinition中的init-method屬性定義等,依賴注入的一個大致過程如圖5.5所示。

圖5.5 IoC容器創建bean及依賴注入序列圖接着我們分析具體的Bean的實例化及依賴注入的實現,具體的實現在Abstract AutowireCapableBeanFactory的doCreate方法中,代碼如下所示:public Object doCreateBean(String beanName,BeanDefinition mbd) {BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {  /** 創建bean對象 */   instanceWrapper = createBeanInstance(beanName, mbd);}FinalObjectbean=(instanceWrapper!=null ?instanceWrapper.getWrappedInstance() : null);Class<?>beanType=(instanceWrapper!=null?instanceWrapper.getWrappedClass() : null);Object exposedObject = bean;/** 依賴注入實現方法 */populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) { /** 初始化bean */   exposedObject = initializeBean(beanName, exposedObject, mbd);}return exposedObject;}以上實現可以看出,具體的Bean實例化是通過createBeanInstance方法創建的,而依賴注入的實現是通過populateBean實現的,最後完成Bean初始化操作。具體的依賴注入是在BeanWrapperImpl的setPropertyValues方法,實現如下:public void setPropertyValues(MutablePropertyValues pvs) {List<PropertyValue> propertyValues = pvs.getPropertyValueList();for (PropertyValue pv : propertyValues) {String propertyName = pv.getName();Object value = pv.getValue();try {    PropertyDescriptor pd = getPropertyDescriptor(propertyName); if (pd != null && pd.getWriteMethod() != null ) { /**得到set方法 */     Method writeMethod = pd.getWriteMethod();     writeMethod.setAccessible(true);     try { /** 通過Java反射機制實現依賴注入  */   writeMethod.invoke(this.object, value);    } catch (IllegalArgumentException e) {e.printStackTrace(); }  }} catch (IntrospectionException e) {e.printStackTrace();}  }}在Bean的創建和對象依賴注入的過程中,需要依據BeanDefinition中的信息來傳遞地完成依賴注入,從上面的實現來看,Bean的依賴注入都是遞歸調用的,體現在依賴注入時,通過遞歸調用容器的getBean方法,得到當前Bean所依賴的Bean,同時也是觸發對依賴Bean的創建及注入,這樣,根據依賴關係,一層一層地完成Bean的創建和注入,直到最後完成當前Bean的創建。5.3.4 TeaIoC整體核心類圖     根據IoC容器所需要的功能,按照面向對象的設計原則,可設計IoC容器的整體核心類圖如圖5.6如下:

圖5.6 TeaIoC容器的整體簡略核心類圖     BeanFactory是容器的核心,它以工廠模式負責bean的創建、管理和銷燬,是bean管理的Facade類。容器中的bean通常會互相合作,因而它們之間會產生依賴關係,BeanFactory在配製文件當中來反映這些依賴關係。BeanFactory有多種實現方式,其中最常用的是用XML配置文件來反映bean之間依賴關係的。容器中不同的類都有特定的職責。比如AbstractAutowireCapableBeanFactory負責bean的創建和管理bean的生命週期;DefaultListableBeanFactory實現了BeanDefinitionRegistry接口能夠在BeanFactory中註冊bean定義信息,並且使IoC核心容器類。BeanDefinitionParserDelegate負責解析XML文件中bean元素的定義;XmlBeanDefinitionReader委託DefaultBeanDefinitionDocumentReader解析整個XML文件。Bean實例的產生有兩種方式:一種是採用Prototype方式,每次調用都產生一個新的實例;另一種是Singleton方式建立單例緩存,如果緩存中不存在實例才創建新實例並將它放到緩存中。5.3.5 從BeanFactory得到 Bean的流程從BeanFactory得到Bean分兩個階段。第一個階段是完成IoC容器的初始化,IoC容器的初始化包括BeanDefinition的載入與解析,BeanDefinition在IoC容器中的註冊。第二個階段是通過BeanDefinition信息,根據適當的實例化策略,生成Bean實例,再執行Bean的生命週期。具體生成Bean實例的流程如圖5.7所示:

圖5.7 IoC容器創建bean的完整流程圖

5.4 IoC框架與MVC框架的整合實現TeaMVC的控制器Action可以由IoC容器來統一管理,要實現這樣的整合功能,必須對於MVC框架提供可配置的接口,對於TeaMVC而言即可以選擇自己來創建Action對象,也可以選擇通過IoC容器來統一管理,TeaMVC的實現方案,是在MVC的配置文件tea-action.xml可以配置開發常量,對應的XML配置文件信息如下:<!--系統開發常量配置,可以定義零個或多個,Action是否由IoC容器來控制--><config-const name="IoC" value="true"/>由上配置信息可以看出,常量IoC爲true時,即把Action對象委託給了IoC容器來管理。具體在DefaultActionInvocation中TeaMVC框架在選擇創建Action的策略時的實現代碼如下:/** 創建Action對象  */public void createAction(){ /** 是否直接從IoC容器取Action對象  */Booleanbool_Const=ConvertUtils.toBoolean(ConfigurationManager.getConst("IoC"));if (bool_Const) { /**直接從IoC容器得到Action對象 */ServletContext servletContext = ServletActionContext.getServletContext();XmlWebApplicationContextwac=(XmlWebApplicationContext)servletContext.getAttribute(XmlWebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);   this.action = wac.getBean(this.proxy.getClassName());}else{ /**直接由TeaMVC來創建Action對象 */    this.action = ClassInstance.newClass(this.proxy.getClassName());}}5.5 IoC框架配置及使用說明爲了使用TeaIoC容器,必須配置核心配置文件tea-context.xml,示例配置如下:<?xml version="1.0" encoding="UTF-8"?><beans><bean id="userDao" class="test.dao.impl.UserDaoImpl"/><bean id="userService" class="test.service.impl.UserServiceImpl"><property name="userDao" ref=”userDao”></bean></beans> 對應的示例類如下:public class UserDaoImpl implement UserDao{    public void add(User user){   System.out.println(“添加一個用戶”);}} public class UserServiceImpl implement UserService{private UserDao userDao;/** 通過TeaIoC依賴注入UserDao對象*/Public void setUserDao(UserDao userDao){   this.userDao = userDao;}public void save(User user){ this.userDao.add(user);}}通過FileSystemXmlApplicationContext來測試IoC容器,具體使用如下:public class Test {public static void main(String[] args) throws SQLException {    /** 讀取TeaIoC的核心配置文件tea-context.xml配置文件,並且執行容器的初始化操作 */FileSystemXmlApplicationContext beanFactory = new FileSystemXmlApplicationContext("tea-context.xml");    /** 從IoC容器中得到已經注入好的userService對象 */UserService userService = beanFactory.getBean("userService");userService.save(new User());       }}

6 輕量級數據持久層框架設計與實現面向對象技術已經成爲軟件開發的一種趨勢,而面向對象的持久性對於大部分的應用來說是使用數據庫技術,在數據庫技術中,關係型數據庫技術又是其中最有影響的技術,當選用關係數據庫作爲持久機制時,就存在一個對象到關係數據庫的映射問題,這是因爲面向對象設計機制與關係模型的不同,造成了面向對象設計與關係數據庫設計之間的阻抗不匹配。面向對象設計基於如耦合、聚合、封裝等理論來表示對象之間的關係,而關係模型則基於數學原理,它主要是通過表的連接、行列的操作來實施數據的存取。習慣了面向對象設計的應用又必須要採用關係型的數據庫作爲持久層開發的方式,ORM框架的產生就是在它們中間架起的橋樑。6.1 數據持久化技術分析6.1.1 JDBC技術    JDBC可以說是訪問持久數據層最原始、最直接的方法。在企業級應用開發中,可使用數據訪問對象(Data Access Object,DAO)模式來把數據訪問封裝起來,然而後在其他層中進行調用。這種方式的優點是運行效率高,缺點是與其他持久化技術相比,JDBC直接訪問數據庫的方式需要程序員做所有的事,自己關心連接池,自己寫大量的get、set方法,把SQL查詢出來的值一個一個賦值到Java對象中,或者把Java對象的值一個一個取出來,用SQL插入到數據庫,完全手動的進行O/R映射,不利於進行面向對象的編程,並且修改、擴展也很複雜。但是JDBC今天還是Java訪問數據庫的基石,我們的TeaORM框架也建立在JDBC的基礎上,對JDBC進行了輕量級的封裝,完全以面向對象的思維操作數據庫,提供了更爲上層的功能。6.1.2 數據持久層分析數據持久層就是爲業務對象提供持久化能力的類的集合。持久層有效的包裝了持久機制。持久層有效的包裝了持久機制。持久層封裝了將對象持久化所需要的行爲,即在持久存儲中讀取、寫入和刪除對象的操作。持久層概念的核心是對象持久化方法,對象持久化的方法有以下三種:(1)一是將結構化查詢語言(SQL)硬編碼到類源碼中。優點是編碼速度快,其缺點是直接耦合了業務類和關係數據庫結構。(2)另一種是將SQL語句從業務類中提取出來封裝在一個或多個“數據類”中。數據的變動只限制在“中間類”中,從而實現解耦的功能。(3)第三種是使用一定的映射規則將對象映射到數據表,根據配置文件轉換成目標SQL語句。這種方法也就是目前持久層普遍採用的對象持久化方法,即O/R映射來實現對象的持久層設計。TeaORM持久層框架採用的是第三種O/R映射(Object Relational Mapping)作爲持久層,O/R映射就是對象模型表示的對象和基於SQL的關係模型之間的一個映射。在具體操作數據庫時,就不需要再去和複雜的JDBC打交道,只需操作對象就可以了。在幾乎所有的程序裏面,都存在對象和關係數據庫。通過持久層的O/R映射就可以直接把對象的信息(數據信息)保存在關係數據庫中。O/R映射關係圖如圖6.1所示。

圖6.1 O/R映射關係圖採用O/R映射實現對象持久層的優點是程序員可以直接以面向對象的思維來操作數據庫,它能極大的提個軟件生產的效率不足是會影響系統的性能。6.1.3 全自動ORM與半自動ORM技術的比較ORM技術從使用者的角度,又可以分爲全自動ORM和半自動ORM。對於全自動ORM,在這類實現中,它將對象到關係的映射進行了完整而又封閉的封裝。半自動ORM只是對映射進行了部分封裝。全自動對象映射的實現共同的特點就是對數據庫結構提供了較爲完整的封裝,提供了從對象到數據表的全套映射機制。開發人員只需定義從對象到數據庫表的映射關係,即可通過全自動ORM框架提供的API來完成持久層操作。在運行時刻,全自動ORM框架會根據配置邏輯生成SQL並調用JDBC來加以執行。這種全面的封裝給開發者帶來了很多好處,但是在許多情況下,完整的封裝也表現出了它的不靈活的一面。半自動對象映射不同於全自動對象映射對數據庫進行全自動化的封裝,而主要定義對象到SQL之間的映射關係,在運行時刻它不會自動生成SQL,具體的SQL需要由程序員來編寫,然後通過映射文件,傳遞SQL所需要的參數,以及返回的結果字段映射到對象。本文的數據持久層TeaORM框架也是採取這種半自動化設計方案。TeaORM框架之所以採用半自動化ORM方案,主要是考慮到與全自動對象關係映射相比,半自動對象關係映射具有如下優點:(1)開發人員可以對數據庫操作細節完全的控制,在數據處理量大、對性能要求比較苛刻的情況下。比較有利於對SQL語句進行優化和調試,避免了數據庫成爲系統的瓶頸。(2)很容易實現動態SQL,每次使用SQL的時候可以隨時定製,極大地方便了開發。(3)系統簡單使用,只要熟悉SQL語句,開發人員能夠馬上上手,降低開發人員學習負擔。6.2數據持久層ORM框架的設計TeaORM能夠大大減少訪問數據庫的代碼,TeaORM可以通過簡單的XML配置文件將JavaBean,Map映射成SQL語句,通過SQL語句的執行而獲得JavaBean,List等對象,TeaORM實現如圖6.2所示。

圖6.2 TeaORM的體系結構圖TeaORM提供了一個簡易的框架,輸入參數使用簡單的XML配置文件映射成JDBC的查詢對象(Statement)— 用來綁定要執行的操作的對象,然後生成結果。TeaORM實現的功能如下:(1)該組件基於XML配置文件,實現底層數據庫操作的SQL可配置化,可以控制最終的數據操作方式,通過SQL的優化獲得最佳的數據庫執行效能,在系統設計上具有比較很大的自由空間。(2) SQL語句的輸入參數可以是基本類型的包裝類型和JavaBean(如Integer、String、Map、JavaBean),輸出參數可以是Integer、Javabean、List。(3)可針對特定的數據庫操作設置特定的Java屬性/SQL字段列值映射。(4) 能夠管理對象的持續性。JavaBean到數據庫表的映射,以對象的方式存取數據。O/R Mapping的定義都基於XML,具有良好的擴展性和通用性。(5)支持靜態SQL和動態SQL。(6)支持增加、修改、刪除、查詢的操作。(7)使用TeaORM框架可以實現數據庫平臺無關性,可以隨時切換開發及數據庫發佈平臺,方便移植。6.3數據持久層ORM框架的實現6.3.1 TeaORM的實現原理TeaORM就是簡化版的SQL工具,其實現與JDBC的實現內容是相似的,用JDBC來對數據庫進行操作,必須要進行如下的操作:(1)加載(註冊)適當的JDBC驅動程序。(2)建立數據庫連接。(3)建立符合JDBC規範的SQL語句和輸入參數。(4)執行SQL語句。(5)處理結果集。(6)關閉數據庫連接。TeaORM底層對數據庫的操作步驟也是一樣的,只是參數信息並不是通過硬編碼來實現的,而是通過配置文件來設置的。TeaORM的實現包括了三個部分:一、SQL語句部分;二、輸入參數部分;三、輸出結果部分。所以,針對上述的JDBC操作,轉換爲TeaORM就變成了如下操作:(1)配置TeaORM配置文件,載入JDBC驅動程序和數據庫連接等信息。(2)配置TeaORM的SQL Map映射文件,包括Parameter、ResultMap、SQL等信息。其中:Parameter是輸入參數部分,ResultMap是輸出結果部分,sql是SQL語句部分。(3)根據SQL Map配置文件信息,加載配置的JDBC驅動程序。(4)根據SQL Map配置文件,建立數據庫連接。(5)根據SQL Map配置文件中的sql信息和Parameter信息,建立符合JDBC規範的SQL語句和輸入參數。(6)執行SQL語句。(7)處理結果集。(8)關閉數據庫連接。6.3.2 SQL語句的解析在SQL Map配置文件的sql信息如下:<!-- delete語句 --><delete id="deleteUserById">    delete from t_user where username = #username# </delete>從以上配置信息,可以看出我們需要執行刪除操作並且傳入的參數爲username,但是配置文件中的SQL語句還不是標準的SQL語句,我們必須通過解析文本信息,解析出符合JDBC標準的SQL語句,例如:delete from t_user where username = ?,也就我們在實現時需要對#username#進行處理,在TeaORM的具體實現如下:/** 解析sql語句,解析出sql語句及參數屬性 */public static String parseSqlText(String sql, List<String> parameterNameList) {StringTokenizer parser = new StringTokenizer(sql, PARAMETER_TOKEN, true);/** 解析後的sql語句 */StringBuffer newSqlBuffer = new StringBuffer();String token = null;String lastToken = null;while (parser.hasMoreTokens()) {token = parser.nextToken();if (PARAMETER_TOKEN.equals(lastToken)) {if (PARAMETER_TOKEN.equals(token)) {        newSqlBuffer.append(PARAMETER_TOKEN);        token = null;} else {parameterNameList.add(token);newSqlBuffer.append("?");token = parser.nextToken();token = null;}} else {if (!PARAMETER_TOKEN.equals(token)) {newSqlBuffer.append(token);}}lastToken = token;}return newSqlBuffer.toString();}6.3.3 Statement的實現TeaORM的SQL Map組件的核心概念是Statement,TeaORM的半自動化的ORM特性也是體現在這裏。Statement可以使用任意的SQL語句,並且擁有Parameter(輸入)和ResultMap(輸出)。有四種類型的Statement,分別對應四種SQL語句,InsertStatement、DeleteStatement、UpdateStatement、SelectStatement四種類型的Statement。Statement組件類的結構如圖6.3所示。

圖6.3 Statement組件類圖  有了以上的實現,我們就可以在SQL Map配置文件中配置如下Statement:   <!-- insert語句 -->   <insert id="insertUser">       insert into t_user(USERNAME,PASSWORD,AGE)       values(#username#,#password#,#age#)   </insert>   <!-- delete語句 -->   <delete id="deleteUserById">       delete from t_user where username = #username#    </delete>   <!-- update語句 -->   <update id="updateUser">       update t_user set password = #password#,GMT_MODIFIED = now() where username = #username#   </update>   <!-- select語句 -->   <select id="selectAllUser" resultMap="userResultMap">       select USERNAME,PASSWORD,AGE,GMT_CREATE,GMT_MODIFIED       from t_user   </select>6.3.4 Parameter輸入參數的實現Parameter傳入的參數可以是JavaBean、基本類型、Map類型。Parameter的基本思想是定義一系列的有序的參數系列,用於匹配JDBC的PrepareStatement的值符號。Parameter的核心作用就是作爲JDBC執行時所需要的參數值,也就是用戶指定的條件,具體從JavaBean中獲取屬性值的實現如下:/** 從JavaBean中取得對應屬性的值 */public static List<Object> getJavaBeanValue(BasicStatement statement,Object parameterObject) {List<Object> valuesParam = new ArrayList<Object>();if (statement != null) {if (statement.getPropertyParameters() != null) {try { /** 從Statement中取得屬性參數列表 */List<String> propertyParamList = statement.getPropertyParameters();/** 得到參數Object的所有屬性的get方法 */Map<String, Method> readMethods = PropertyDescriptorManager.getReadMethods(parameterObject.getClass());if (propertyParamList != null && propertyParamList.size() > 0) {   for (int i = 0; i < propertyParamList.size(); i++) {                   Method readMethod = readMethods.get(propertyParamList.get(i));                   /** 從JavaBean中讀取參數屬性的值*/                   Object value = readMethod.invoke(parameterObject,new Object[0]);                    /** 把得到的值存入List中 */                   valuesParam.add(value);} return valuesParam;             }        }    }}6.3.5 ResultMap輸出結果的實現  ResultMap在執行SelectStatement時,ResultMap負責將結果集的列值映射成JavaBean的屬性值,ResultMap的id屬性是statement的唯一標識。ResultMap的class屬性用於指定Java類的完全限定名。ResultMap可以包括任意多的屬性映射,將查詢結果集的列值映射成JavaBean的屬性。屬性的映射按它們在ResultMap中定義的順序進行。相關JavaBean類必須符合JavaBean規範,每一個屬性都必須擁有get/set方法。具體的映射實現如下:/** 得到JavaBean對象,根據resultMap映射關係,設置相應的屬性值 */public Object getJavaBean(ReultMap resultMap,BasicStatement statement,ResultSet result) {/** 創建JavaBean對象 */Object resultObject = ClassInstance.newClass(resultMap.getClassName());Map<String, Method> writeMethods;try { /**得到ResultSet中的列名總數 */int Columncount = result.getMetaData().getColumnCount();/** 這裏使用HashSet,因爲後面使用contains的時候效率高 */Set<String> columnNames = new HashSet<String>();for(int i=0;i<Columncount;i++)  columnNames.add(result.getMetaData().getColumnName(i+1));/**根據JavaBean的Class對象得到所有屬性的set方法 */writeMethods = PropertyDescriptorManager.getWriteMethods(resultObject.getClass());/** 注意,這裏得到resultMap映射關係  property-->column */Map<String,String> mapColumnsMap = resultMap.getResultMappings();String column;for (String property : mapColumnsMap.keySet()) { /** 根據property取得colum */column = mapColumnsMap.get(property);/** 判斷ResultSet中是否有此字段名,如果select語句中沒有此字段,就不需要給JavaBean設置屬性 */if(columnNames.contains(column)){ /** 根據column名得到查詢的值 */Object value = result.getObject(column);/** value必須要爲非空,因爲如user.setAge(int a)如果參數值爲null,就會參數java.lang.IllegalArgumentException異常 *//** value如果爲空,設置不設置都一樣 */if (value != null) { /** 得到set方法 */Method writeMethod = writeMethods.get(property);                writeMethod.invoke(resultObject, value); /** 向JavaBean中設置屬性 */}}}}6.4 IoC框架與ORM框架整合實現IoC容器在O/R映射中的整合應用,只要是用在數據庫的連接管理上,對數據庫的連接方面,一般有專用的數據庫連接池組件(比如Apache的dbcp連接池)來獲取數據庫連接,應用IoC組件,提供用戶簡單的接口,用戶只需要簡單的實現配置就可以,實現靈活的切換連接池,大大的擴展了系統的可擴展性。這裏簡單看一下配置文件,就可以看出IoC容器在TeaORM框架中應用的靈活度。TeaIoC的核心配置文件tea-context.xml配置如下:<beans><!-- 配置apache下的dbcp數據庫連接池,默認連接池大小爲8 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/AMS" /><property name="username" value="root" /><property name="password" value="root" /></bean><!-- 配置TeaORM框架的JdbcTemplate --><bean id="jdbcTemplate" class="org.teaframework.orm.support.JdbcTemplate" init-method="init"><property name="dataSource" ref="dataSource" /><!—配置ORM框架的主配置文件--><property name="configLocation"><value>tea-orm.xml</value> </property></bean><!-- 配置DAO,直接把jdbcTempate注入進去 --><bean id="adminDao" class="org.ams.dao.imp.AdminDaoImp"><property name="jdbcTemplate" ref="jdbcTemplate" /></bean>從配置文件可以看出,數據庫連接池是可配置的,即隨時可以切換數據庫連接池及數據庫連接信息。dataSource的具體配置和初始化是在IoC容器啓動時完成的,通過簡單的配置無需用戶完成代碼就可以實現數據庫的連接工作。如果現在要換具體的數據庫或具體的連接池實現,這時應用IoC容器,只需要簡單的修改dataSource的配置就可以滿足。原因在於jdbcTemplate是利用IoC容器注入dataSource實例的,所以jdbcTemplate及應用的DAO根本不需要關注具體的dataSource來自何處、由誰實現。6.5 ORM框架配置及使用說明    爲了使用TeaORM框架,必須配置ORM的核心配置文件tea-orm.xml,配置示例如下所示:<tea-OrmConfiig>        <!-- name數據庫的用戶名 -->        <!-- password數據庫的密碼 -->        <!-- dbName需要訪問的數據庫的名字 -->    <connection name="root" password="root" dbName="ibatis" ><dataBaseVersion value="MySQL"/>    <driver value="com.mysql.jdbc.Driver"/> <url value="jdbc:mysql://localhost:3306/tea"/> </connection>        <!--是否在控制器輸出Sql語句 --> <config-const name="show_sql" value="true"/> <!-- sqlMap映射文件  --><sqlMap resource="test/model/UserSqlMap.xml"/></tea-OrmConfiig>然後配置實體類User對應的SqlMap配置文件,示例配置如下:<sqlMap namespace="userDao"><resultMap id="userResultMap" class="test.model.User"><result property="username" column="USERNAME" /><result property="password" column="PASSWORD" /><result property="age" column="AGE" /><result property="gmtCreate" column="GMT_CREATE" /><result property="gmtModified" column="GMT_MODIFIED" /></resultMap>   <!-- insert語句 -->   <insert id="insertUser">       insert into t_user(USERNAME,PASSWORD,AGE)       values(#username#,#password#,#age#)   </insert>   <!-- delete語句 -->   <delete id="deleteUserById">       delete from t_user where username = #username#    </delete>   <!-- update語句 -->   <update id="updateUser">       update t_user set password = #password#,GMT_MODIFIED = now() where username = #username#   </update>   <!-- select語句 -->   <select id="selectAllUser" resultMap="userResultMap">       select USERNAME,PASSWORD,AGE,GMT_CREATE,GMT_MODIFIED       from t_user   </select></sqlMap>現在所有的ORM配置文件已經配置完成,我們就可以通過TeaORM來操作數據庫了,具體的使用代碼如下:public class TestClass {public static void main(String[] args) { // 得到用戶接口SqlMapClient對象SqlMapClient sqlMapClient = SqlMapClientBuilder.buildSqlMapClient("tea-orm.xml");User user = new User();user.setUsername("cui");user.setPassword("7999502");user.setAge(23);sqlMapClient.insert("userDao.insertUser",user); //添加user.setPassword("xiu"); //修改 sqlMapClient.update("userDao.updateUser",user);sqlMapClient.delete("userDao.deleteUserById", "cui"); //刪除List users = sqlMapClient.queryForList("userDao.selectAllUser"); //查找 System.out.println(users);}7 輕量級TeaFramework框架的應用7.1 系統分析7.1.1 資產管理系統簡介資產管理系統爲企業提供全面、迅速的資產信息,方便管理者瞭解和操作企業內部的資產管理。本系統是基於B/S的多層Web應用,在windows xp操作系統下,本系統選擇了Myeclipse8.0開發環境、Mysql數據庫和JSP、本論文輕量級的TeaFramework框架、AJAX無刷新技術,利用MVC設計模式將業務邏輯和表示邏輯分離,在表示層利用JSP技術實現了頁面展現、利用AJAX實現無刷新技術;在業務邏輯層,利用TeaIoC框架技術實現了系統設置、資產管理以及分類統計三個主要模塊的開發;後臺使用Mysql進行數據庫的開發,並利用TeaORM技術完成對數據庫的操作,實現了數據查詢、修改、增加、刪除等功能。資產管理系統應用了輕量級Tea框架的所有功能,可以充分說明利用TeaFramework框架對WEB系統開發的作用和意義。7.1.2 資產管理系統功能介紹 本系統有3個用戶,即普通用戶(簡稱用戶)、管理員與超級管理員。用戶僅僅作爲資產擁有者而存在,用戶的信息由管理員錄入;管理員可以管理用戶、部門、類別、資產。超級管理員除了擁有管理員的權限外,還可以管理管理員。資產管理系統用例圖如圖7.1所示。 系統用例主要包括:管理員可以管理系統(管理用戶、管理部門、管理類別、管理管理員)、管理資產(增加資產、刪除資產、修改資產、查詢資產)、統計分類(統計資產、統計送修)等。

圖7.1資產管理系統用例圖7.1.3 資產管理系統數據分析本商城購物系統主要包括6個實體:(1)資產(資產id,資產編號,名稱,類別,型號,生產廠家,生產日期,購買日期,價格,使用情況,折舊,所屬部門,所屬用戶)(2)類別(類目id,類別名,描述)(3)部門(部門id,部門名,描述)(4)用戶(用戶id,姓名,所屬部門,郵箱,電話,手機)(5)送修(送修id,資產id,送修日期,花費,狀態,送修人員,描述)(6)管理員(管理員id,用戶名,密碼,姓名,登錄次數,所屬部門,郵箱,電話,手機,權限,最後登錄時間)各個實體之間的關係爲:某個用戶可以擁有多個資產;一個類別中可以包括多個資產;一個資產可以維修多次,一次送修只對應一個資產。7.2 系統設計7.2.1 系統總體結構設計整個系統的架構設計遵循MVC模式,將展示層、控制層、模型層(業務邏輯層)及持久層進行合理分離。表現層採用TeaMVC框架,業務邏輯層採用TeaIoC框架來管理系統中的業務邏輯,數據持久層以半自動化TeaORM框架,提供具體的數據庫數據處理操作,三者進行有機整合,構成輕量級J2EE應用框架。整個輕量級框架業務流程爲:用戶接口層利用JSP+HTML頁面實現交互界面,負責傳送頁面請求和接收響應,表示層TeaMVC收到請求,調用相應的控制器Action,處於業務層的TeaIoC容器負責向Action提供業務服務組件(Service)和相應的數據訪問處理組件(DAO),處於持久層的TeaORM負責對象化映射與數據庫交互,具體處理DAO組件請求,並返回結果。資產管理系統體系結構圖如圖7.2所示。

圖7.2資產管理系統體系結構圖7.2.2數據庫設計將系統中的實體根據關係轉換原則,併兼顧操作的靈活性,可以將6個實體轉化爲以下6個關係表,如表7.1~表7.6所示。表7.1  管理員表(admin)

字段名稱

數據類型

是否爲空

主鍵

描述

id

Int

Not null

主鍵

username

Varchar

Not null

用戶名

password

Varchar

Not null

密碼

字段名稱

數據類型

是否爲空

主鍵

描述

name

Int

Not null

姓名

logincount

Int

Not null

登錄次數

department

Int

Not null

所屬部門

email

Datetime

郵箱

workphone

Varchar

電話

mobilephone

Varchar

手機

permission

Int

Not null

權限

lasttime

Datetime

Not null

最後登錄時間

表7.2 資產表(assets)

字段名稱

數據類型

是否爲空

是否允許爲空

描述

id

Int

Not null

主鍵

aid

Varchar

Not null

編號

assetname

Varchar

Not null

名稱

type

Int

Not null

外鍵

類別

version

Varchar

型號

manufacturer

Varchar

生產廠家

manufacturedate

Datetime

生產日期

buydate

Datetime

Not null

購買日期

price

Double

Not null

價格

usestate

Int

Not null

使用情況

deprecition

Int

Not null

折舊

department

Int

外鍵

所屬部門

user

Int

外鍵

所屬用戶

表7.3 類別表(type)

字段名稱

數據類型

是否爲空

主鍵

描述

id

Int

Not null

主鍵

typename

Varchar

Not null

類別名

description

Text

描述

    表7.4 部門表(department)

字段名稱

數據類型

是否爲空

主鍵

描述

id

Int

Not null

主鍵

dpname

Varchar

Not null

部門名

description

Text

描述

表7.5 用戶表(user)

字段名稱

數據類型

是否爲空

主鍵

描述

id

Int

Not null

主鍵

name

Varchar

Not null

姓名

department

Int

Not null

外鍵

所屬部門

email

Varchar

郵箱

workphone

Varchar

電話

mobilephone

Varchar

手機

表7.6 送修表(bsend)

字段名稱

數據類型

是否爲空

主鍵

描述

id

Int

Not null

主鍵

aid

Int

Not null

外鍵

資產ID

bSenddate

Datetime

Not null

送修日期

cost

Double

Not null

花費

state

Int

Not null

狀態

bSendperson

Int

Not null

外鍵

送修人員

description

Text

描述

7.3 系統實現7.3.1 TeaFramework在資產管理系統中的應用7.3.1.1 TeaMVC與Web環境的整合配置     爲了在資產管理系統中應用TeaMVC框架,必須在Servlet規範文件web.xml中引入TeaMVC的核心過濾器TeaFrameworkFilter及一些輔助過濾器,如亂碼處理過濾器CharacterEncodingFilter,這樣就把TeaMVC的入口引入到了資產管理系統中,之後就可以處理客戶端的Http請求,統一進入框架處理配置時需要特別注意一點,就是亂碼處理過濾器一定要配置在覈心過濾器TeaFrameworkFilter之前,因爲一旦進入核心過濾器處理,就不會再順着過濾器鏈流向下一個過濾器,即其它過濾器就不會起到攔截的作用了。TeaMVC在web.xml的配置如下:  <!-- 利用的TeaMVC框架的過濾器來設置編碼方式 -->  <filter><filter-name>encodingFilter</filter-name><filter-class>org.teaframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>  </filter>  <filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern>  </filter-mapping>

  <!-- 配置TeaMVC框架的入口Filter -->  <filter>   <filter-name>actionFilter</filter-name>   <filter-class>org.teaframework.web.filter.TeaFrameworkFilter</filter-class>   <init-param>   <param-name>actionConfig</param-name>

<!-- 配置TeaMVC的核心配置文件 -->   <param-value>tea-action.xml</param-value>   </init-param>  </filter>  <filter-mapping>   <filter-name>actionFilter</filter-name>   <url-pattern>/*</url-pattern>  </filter-mapping>7.3.1.2 TeaMVC的應用配置      TeaMVC的核心配置文件爲tea-action.xml,該配置文件中可以配置開發常量,控制器Action模塊,用戶自定義攔截器,默認攔截器,全局result模塊,資產管理系統的TeaMVC的核心配置文件tea-action.xml如下:<tea-web><!-- 系統開發常量配置,可以定義零個或多個,Action是否由IoC容器來控制--><config-const name="IoC" value="true"/><!-- 配置可編程控制器模塊 --><controller name="admin" namespace="/">   <!-- 攔截器配置 -->   <interceptors> <interceptor name="checklogin" class="org.ams.interceptor.CheckLoginInterceptor" /><interceptorname="checkpermission"class="org.ams.interceptor.CheckPermissionInterceptor" /> <interceptor-stack name="withchecklogin"><interceptor-ref name="checklogin" /><interceptor-ref name="defaultStack" /> </interceptor-stack>   </interceptors>   <!-- 默認攔截器 -->   <default-interceptor-ref name="withchecklogin" />   <!-- 全局result配置 -->   <global-results><result name="login" url="/index.jsp" /><result name="permission" url="/nopermission.jsp"/><result name="error" url="/error.jsp"/>   </global-results>

<!-- 配置action --><action name="createtype" class="createTypeAction" method="create" /><action name="createadmin" class="createAdminAction" method="create"><action name="createuser" class="createUserAction" method="create" /><action name="createasset" class="createAssetAction" method="create" /><!-- admin --><action name="createadmin" class="createAdminAction" method="create"><interceptor-ref name="checkpermission" /><interceptor-ref name="defaultStack" /></action><action name="searchadmin" class="searchAdminAction" method="search"><interceptor-ref name="checkpermission" /><interceptor-ref name="defaultStack" /></action><action name="deleteadmin" class="deleteAdminAction" method="delete"><interceptor-ref name="checkpermission" /><interceptor-ref name="defaultStack" /></action>        ……<!-- login及exit及statistics --><action name="login" class="loginAction" method="login" /><action name="exit" class="exit" method="exit" />

<!-- 開發約定必順在WEB-INF下的errorPage文件夾下 --><errorPage><page name="404" url="errorNotFoundResultAction.jsp" /></errorPage>

</tea-web>7.3.1.3 TeaMVC攔截器實現登錄及權限驗證     基本的需求是管理員向服務器發起一個請求時,系統通過攔截配置去判斷是否已經登錄,如果還沒登錄,則不會執行業務邏輯,而是直接攔截下來轉發到登錄頁面。TeaMVC攔截器實現驗證是否已經登錄的基本原理圖如圖7.3所示。

圖7.3 TeaMVC攔截器實現是否已登錄圖具體的攔截器實現如下:/** 使用TeaMVC攔截器  驗證是否已經登錄  */public class CheckLoginInterceptor implements Interceptor {public String intercept(ActionInvocation invocation) throws Exception {    if (invocation.getAction() instanceof LoginAction) {            return invocation.invoke();         }    Map session = invocation.getInvocationContext().getSession();    Object admin = session.get("admin");    /** 驗證是否已經登錄 */    if(admin != null){ /** 如果已經登錄則繼續執行下去  */    return invocation.invoke();    }    else{ /** 如果沒有登錄則跳轉到登錄頁面index.jsp */    return "login";    }    }}     實現管理員權限驗證也是相同的原理。TeaMVC攔截器實現權限驗證基本原理如圖7.4所示。

圖7.4 TeaMVC攔截器實現管理員權限驗證圖具體的攔截器實現如下:/** 使用TeaMVC攔截器 驗證權限 */public class CheckPermissionInterceptor implements Interceptor {public String intercept(ActionInvocation invocation) throws Exception{    Map session = invocation.getInvocationContext().getSession();    /** 先驗證是否已經登錄 */    if(session.get("admin") == null)     return "login";    Admin admin = (Admin)session.get("admin");    if(admin.getPermission() == 0){ /** 如果是超級管理員,則繼續執行下去*/    return invocation.invoke();    }    else{/** 如果不是超級管理員,則權限不夠,跳轉到無權限頁面 */    return "permission";    }    }}具體在TeaMVC的核心配置文件tea-action.xml中的配置如下:<!--攔截器配置  --><interceptors>     <!—配置登錄驗證攔截器--> <interceptor name="checklogin" class="org.ams.interceptor.CheckLoginInterceptor" />     <!—配置權限驗證攔截器--><interceptorname="checkpermission"class="org.ams.interceptor.CheckPermissionInterceptor" /> <interceptor-stack name="withchecklogin">     <interceptor-ref name="checklogin" />     <interceptor-ref name="defaultStack" /> </interceptor-stack></interceptors>

<!--默認攔截器配爲登錄驗證攔截器  --><default-interceptor-ref name="withchecklogin" />7.3.1.4 TeaORM的應用配置TeaORM作爲半自動化的ORM框架,爲資產管理系統提供了持久層的解決方案,資產管理系統的TeaORM的核心配置文件tea-orm.xml如下:<?xml version="1.0" encoding="UTF-8"?><tea-OrmConfig>    <!-- 是否打印sql語句 -->   <config-const name="show_sql" value="false" />    <!-- SqlMap配置文件,核心JDBC操作及OR映射關係都配置以下配置文件中 -->    <sqlMap resource="org/ams/model/AdminSqlMap.xml" />    <sqlMap resource="org/ams/model/DepartmentSqlMap.xml" />    <sqlMap resource="org/ams/model/TypeSqlMap.xml" />     <sqlMap resource="org/ams/model/UserSqlMap.xml" />     <sqlMap resource="org/ams/model/AssetsSqlMap.xml" />     <sqlMap resource="org/ams/model/BsendSqlMap.xml" />  </tea-OrmConfig>TeaORM具體的核心OR配置文件*SqlMap,起到了資產管理系統中的數據對象DO與數據表之間的映射關係的對應配置,並且系統所需要對這個的DO的增刪查改操作可以直接在該DO對應的SqlMap配置文件中進行直接的SQL語句的編寫,爲系統設計提供了更大的自由空間,爲以後SQL語句優化提供了便利,資產管理系統中數據對象Type對應的TypeSqlMap.xml配置文件如下:<?xml version="1.0" encoding="UTF-8" ?><sqlMap namespace="typeDao">   <!--配置對象數據庫映射關係--><resultMap id="typeResultMap" class="org.ams.model.Type"><result property="id" column="id" /><result property="typename" column="typename" /><result property="description" column="description" /></resultMap><!-- insert語句 --><insert id="insertType">     insert into type(typename,description)     values(#typename#,#description#)</insert>   <!-- delete語句 --><delete id="deleteType">     delete from type where id = #id#</delete><!-- update語句 --><update id="updateType">     update type set typename = #typename#,description = #description# where id = #id#</update><!-- select語句 --><select id="selectTypeById" resultMap="typeResultMap">     select * from type     where id = #id#</select>   </sqlMap>7.3.1.5 TeaIoC的應用配置TeaIoC作爲資產管理系統的IoC容器框架,管理着系統中可配置的bean,資產管理系統的TeaIoC框架的配置文件tea-context.xml如下:<beans><!-- 配置apache下的dbcp數據庫連接池,也叫數據源,默認連接池大小爲8 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/AMS" /><property name="username" value="root" /><property name="password" value="root" /></bean>    <!-- 配置TeaORM框架的JdbcTemplate --><bean id="jdbcTemplate" class="org.teaframework.orm.support.JdbcTemplate" ><property name="dataSource" ref="dataSource" /><property name="configLocation"><value>tea-orm.xml</value> <!--TeaORM框架的核心配置文件--></property></bean><!-- 配置Dao --><bean id="adminDao" class="org.ams.dao.imp.AdminDaoImp"><property name="jdbcTemplate" ref="jdbcTemplate" /></bean>    ......    <!-- 配置Service --><bean id="adminService" class="org.ams.service.imp.AdminServiceImp"><property name="adminDao" ref="adminDao" /></bean>    ……<!-- 配置Action --><bean id="createAdminAction" class="org.ams.action.admin.CreateAdminAction" scope="prototype"><property name="adminService" ref="adminService" /></bean></beans>7.3.1.6 TeaIoC和TeaMVC的整合配置     TeaMVC中的控制器Action對象可以由TeaMVC框架自己創建,也可以由TeaIoC框架來託管,請求Action時,TeaMVC只需要去向IoC容器取對應的Action對象。在資產管理系統中控制器Action由IoC容器來統一託管,整合配置在tea-action.xml中如下:<tea-web><!—在tea-web.xml中通過開發常量IoC爲true指定控制器統一由IoC容器管理 --><config-const name="IoC" value="true"/>   <!--配置action ,這裏class即爲TeaIoC配置文件中對應的bean的id--><action name="createtype" class="createTypeAction" method="create" />….</tea-web>在TeaIoC的配置文件tea-context.xml中對應的A ction的bean配置如下。<bean id="createTypeAction" class="org.ams.action.type.CreateTypeAction" scope="prototype"><property name="typeService" ref="typeService" /></bean>7.3.1.7 TeaIoC和TeaORM的整合配置TeaORM作爲一個獨立的ORM框架,可以單獨配置數據源,也可以與TeaIoC框架整合,通過bean來管理數據源。在資產管理系統中我們採用後者與TeaIoC整合的方案來來配置系統的數據源。用戶的DAO組件可以直接集成TeaORM提供的JdbcDaoSupport父類,再通過TeaIoC向DAO組件注入JdbcTemplate,則直接調用getJdbcTemplate()方法就可以進行數據庫操作。tea-context配置文件如下:<!-- 配置apache下的dbcp數據庫連接池,默認連接池大小爲8 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost:3306/AMS" /><property name="username" value="root" /><property name="password" value="root" /></bean>

<!-- 配置TeaORM框架的JdbcTemplate --><bean id="jdbcTemplate" class="org.teaframework.orm.support.JdbcTemplate" ><property name="dataSource" ref="dataSource" /><property name="configLocation"><!--配置TeaORM的配置文件,把TeaIoC和TeaORM整合起來-->     <value>tea-orm.xml</value> </property></bean>用戶的DAO組件只需要如下編寫:public class AdminDaoImp extends JdbcDaoSupport implements AdminDao{ /** 創建Admin */public void add(Admin admin){getJdbcTemplate().insert("adminDao.insertAdmin", admin);}}7.3.2 系統功能模塊的設計與實現7.3.2.1 資產管理系統工程包層次結構資產管理系統工程包層次結構如圖7.5所示。

7.5資產管理系統工程包層次結構圖7.3.2.2資產添加模塊的設計與實現管理員可以添加和查詢資產。管理員添加資產具體的序列圖如圖7.6所示。

  圖7.6 添加資產序列圖

關鍵源代碼及註釋如下所示:/** 創建資產 */public String create() throws IOException, ParseException{ServletResponse response = ServletActionContext.getServletResponse();response.setCharacterEncoding("utf-8");PrintWriter out = response.getWriter();if(typeid == 0 || asset.getAid().equals("") || asset.getAssetname().equals("") || asset.getPrice().equals("")){out.print("empty");return null;}if(isuse.length() > 1 || isdep.length() > 1 || gmrq.equals("")){out.print("empty");return null;}if(Integer.valueOf(isuse) == 2){if(did == 0 || uid == 0){out.print("empty");return null;}}String name, mp = "";if(!ccrq.equals("")){Date date = DateFormat.getDateInstance().parse(ccrq);asset.setManufacturedate(new Timestamp(date.getTime()));}if(!gmrq.equals("")){Date date = DateFormat.getDateInstance().parse(gmrq);asset.setBuydate(new Timestamp(date.getTime()));}asset.setAssetname(name);if(!asset.getManufacturer().equals(""))asset.setManufacturer(mp);asset.setUsestate(Integer.valueOf(isuse));asset.setDeprecition(Integer.valueOf(isdep));assetService.add(asset, typeid, did, uid);out.print("suc");return null;}該功能在tea-action.xml中的配置如下:<action name="createasset" class="createAssetAction" method="create" />該功能在tea-context.xml中的配置如下:<bean id="createAssetAction" class="org.ams.action.asset.CreateAssetAction"scope="prototype">    <property name="assetService" ref="assetService" /></bean>該功能在assetSqlMap.xml中配置如下:<insert id="insertAssets"> insertintoassets(aid,assetname,version,manufacturer,manufacturedate,buydate,price,usestate,deprecition,type,department,user)values(#aid#,#assetname#,#version#,#manufacturer#,#manufacturedate#,#buydate#,#price#,#usestate#,#deprecition#,#typeId#,#departmentId#,#userId#) </insert>該功能實現的效果如圖7.7所示。

         圖7.7 添加資產效果圖7.3.2.3資產查詢模塊的設計與實現管理員可以查詢資產。管理員查詢資產具體的序列圖如圖7.8所示。

  圖7.8 查詢資產序列圖關鍵源代碼及註釋如下所示:/** 根據資產編號或資產名稱查詢資產 */@SuppressWarnings("unchecked")public String search() throws IOException{ServletResponse response = ServletActionContext.getServletResponse();PrintWriter out = response.getWriter();if(selecttype.length() > 1){out.print("empty");return null;}if(Integer.valueOf(selecttype) == 1){ /** 根據資產編號查詢 */List<Assets> list = assetService.searchByAid(value, typeid, isdep, isuse, price1, price2, did, uid);for(Assets asset : list){asset.setType(typeService.getTypeById(asset.getTypeId()));        asset.setDepartment(departmentService.getDepartmentById(asset.getDepartmentId()))asset.setUser(userService.getUserById(asset.getUserId()));}JSONArray json = JSONArray.fromObject(list);out.print(json.toString());return null;}if(Integer.valueOf(selecttype) == 2){ /** 根據資產名稱查詢 */List<Assets> list = assetService.searchByName(value, typeid, isdep, isuse, price1, price2, did, uid);for(Assets asset : list){asset.setType(typeService.getTypeById(asset.getTypeId()));            asset.setDepartment(departmentService.getDepartmentById(asset.getDepartmentId()))asset.setUser(userService.getUserById(asset.getUserId()));}JSONArray json = JSONArray.fromObject(list);out.print(json.toString());return null;}return null;}該功能在tea-action.xml中的配置如下:<action name="searchasset" class="searchAssetAction" method="search" />該功能在tea-context.xml中的配置如下:<bean id="createAssetAction" class="org.ams.action.asset.CreateAssetAction"scope="prototype">    <property name="assetService" ref="assetService" /></bean>該功能在assetSqlMap.xml中配置如下:<select id="selectByAid" resultMap="assetsResultMap">    select * from assets    where aid like concat('%',#aid#,'%')</select>該功能實現的效果如圖7.9所示。

圖7.9 查詢資產效果圖7.3 利用TeaFramework實現資產管理系統的效果利用TeaFramework框架實現資產管理系統,使得該業務系統具有了許多優良的特性,具體有:(1)TeaFramework框架使得該管理系統的結構清晰,功能明確利用TeaFramework框架開發的管理系統,使得管理系統的所有功能都清晰的配置在配置文件中,而且Model、View、Controller的結構非常清晰,提高了整個系統的可理解性。(2)TeaFramework框架提高了該管理系統的可擴展性利用TeaFramework框架開發的管理系統,使得管理系統的可擴展性大大提高,如每需要增加一個功能,只需修改配置文件,增加幾個相應的動作即可,而不必過多關注各模塊交互的細節。(3)TeaFramework框架提高了管理系統開發的效率TeaFramework框架給管理系統提供了清晰的架構,該業務系統的再次開放者只需填充、完善相應的模塊的功能,而不需要編寫模塊之間交互的代碼,還有TeaFramework框架的控制器Action完成了WEB應用系統的大部分功能,系統的開發者只需要編寫相應的配置文件即可,從而極大的提高了WEB應用系統的開發效率。

總結與展望目前,輕量級框架的發展非常的繁榮,輕量級框架的發展極大的推動了J2EE技術的發展,但沒有那一個輕量級框架是十分完美的,而且我們的J2EE項目對各個輕量級框架的要求也不盡相同,即使是針對同一個框架,其提供的功能也不會很符合我們不同項目的要求,我們的目標就是實現一個快速搭建軟件的框架或者是平臺,這個平臺是一個比較實用和通用的軟件開發半成品框架,一方面應用框架能迅速的實現軟件的構建;另一方面也能根據項目的需要快速的搭配、調整和定製與項目適配的框架,從而實現真正軟件快速生成,實現軟件工廠的目的。本文主要討論的就是研究和實現這樣一個框架,雖然在實現上還有許多的問題離軟件平臺的目標還有許多的差距,但還是做了些工作和嘗試,下面謹對本論文的工作做如下概括: (1)根據目前主流的開源框架(Struts2、Spring、iBatis)的思想提出了J2EE輕量級框架整體的分層結構的設計,並給出了各層相應的框架組件的設計。(2)詳細的給出了各個層次的具體設計和實現,具體實現了表現層TeaMVC,業務層TeaIoC框架,數據持久層TeaORM框架。(3)在實現的表現層和數據層框架中,不僅給出了表現層MVC框架和數據層框架ORM的設計和實現,另外還在該實現中都應用了我們設計的中間層框架的實現。實際上整合了前面設計的中間層組件IoC框架組件。(4)本論文的TeaFramework雖然一些設計思想及設計理念來自於現在主流的開源框架,但是也有自己的特色,就是把所有層次的框架功能都一站式地整合起來,不需要額外的插件,能快速地開發軟件半成品。(5)最後通過應用一個資產管理系統,實踐了TeaFramework作爲一個輕量級框架的通用的快速開發框架的穩定性及可行性。由於時間有限,論文的研究和實現工作仍處於初期階段,有許多工作有待完成,需要繼續補充、完善。下一步要做的工作如下。(1)進一步完善整個框架的結構,從總體上把握和協調好各個框架組件的關係,能實現按需要動態的配置和替換各個框架組件。(2)對已經實現的框架,還需要近一步的完善其功能,改善其性能,具體爲:對MVC框架根據頁面驅動進行重構;對IoC框架目前實現的功能還有待擴展。(3)後期會增加業務層AOP框架,這個組件已經構思好,很快可以設計及實現。由於作者水平有限對有些問題未能深入探討錯誤與不妥之處在所難免,敬請各位評閱專家和老師們批評指正。
TeaFramework下載地址:http://download.csdn.net/detail/c929833623lvcha/5255385
TeaFrameworkTest下載地址:http://download.csdn.net/detail/c929833623lvcha/5255417




發佈了49 篇原創文章 · 獲贊 30 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章