第一篇:ssh框架的構(gòu)成分析和代碼構(gòu)架小結(jié)
ssh框架的構(gòu)成分析和代碼構(gòu)架小結(jié)
最近在弄j2ee,發(fā)現(xiàn)還是學(xué)到了很多東西,心情happy啊!昨天看到了一個(gè)文章是關(guān)于ssh的,就弄下來了,后面加點(diǎn)自己的東西,很感謝網(wǎng)上的各位前輩的總結(jié)。一個(gè)spring2.5+hibernate3.2+struts2.0組合框架,使用spring的IoC來管理應(yīng)用的所有bean,包括struts2的action,充分發(fā)揮了spring輕量級框架的優(yōu)勢。
摘 要:針對當(dāng)前Web應(yīng)用程序開發(fā)面臨的問題,結(jié)合目前比較流行的開源框架Spring、Struts和Hibernate,提出了一種開發(fā)J2EE Web應(yīng)用的輕量級解決方案,以幫助開發(fā)人員在短期內(nèi)搭建結(jié)構(gòu)清晰、可復(fù)用性好、維護(hù)方便的Web應(yīng)用程序。并且,通過案例具體說明了如何將這一方案應(yīng)用到實(shí)際項(xiàng)目中。
關(guān)鍵詞:J2EE MVC Struts Spring Hibernate
大型企業(yè)級Web應(yīng)用系統(tǒng)的開發(fā)通常要求有一個(gè)良好的軟件架構(gòu)、便于協(xié)作開發(fā)和擴(kuò)展升級,而傳統(tǒng)的開發(fā)模式不能很好地滿足這些要求。本文針對當(dāng)前Web應(yīng)用程序開發(fā)面臨的問題,結(jié)合目前比較流行的開源框架SSH(Spring、Struts、Hibernate),提出一種開發(fā)J2EE 企業(yè)級Web應(yīng)用的輕量級解決方案,并通過案例具體說明如何將這一方案應(yīng)用到實(shí)際項(xiàng)目中。1 框架技術(shù)
著名的軟件大師Ralph Johnson對框架(Framework)進(jìn)行了如下的定義: 框架是整個(gè)系統(tǒng)或系統(tǒng)的一部分的可重用設(shè)計(jì),由一組抽象的類及其實(shí)例間的相互作用方式組成。
框架一般具有即插即用的可重用性、成熟的穩(wěn)定性以及良好的團(tuán)隊(duì)協(xié)作性。J2EE復(fù)雜的多層結(jié)構(gòu)決定了大型的J2EE項(xiàng)目需要運(yùn)用框架和設(shè)計(jì)模式來控制軟件質(zhì)量。目前,市場上出現(xiàn)了一些商業(yè)的、開源的基于J2EE的應(yīng)用框架,其中主流的框架技術(shù)有:基于MVC模式的Struts框架和基于IoC模式的Spring框架以及對象/關(guān)系映射框架Hibernate等。1.1 表示層框架Struts
Struts是一個(gè)在JSP Model2基礎(chǔ)上實(shí)現(xiàn)的MVC框架,主要分為模型(Model)、視圖(Viewer)和控制器(Controller)三部分,其主要的設(shè)計(jì)理念是通過控制器將表現(xiàn)邏輯和業(yè)務(wù)邏輯解耦,以提高系統(tǒng)的可維護(hù)性、可擴(kuò)展性和可重用性。Struts框架的體系結(jié)構(gòu)如圖1所示。
[2]
[1]
下面就圖1所示的體系結(jié)構(gòu)圖分析Struts框架中的MVC組件。
(1)視圖:視圖部分主要由JSP頁面組成,其中沒有流程邏輯、業(yè)務(wù)邏輯和模型信息,只有標(biāo)記。Struts自身包含了一組標(biāo)記庫(TagLib),這也是Struts的精華之一,靈活運(yùn)用它們可以簡化JSP頁面的代碼,提高開發(fā)效率。
(2)控制器:Struts中的Controller主要是其自身提供的ActionServlet。ActionServlet接收所有來自客戶端的請求并根據(jù)配置文件(struts-config.xml)中的定義將控制轉(zhuǎn)移到適當(dāng)?shù)腁ction對象。
(3)模型:Struts沒有定義具體Model層的實(shí)現(xiàn),Model層通常是和業(yè)務(wù)邏輯緊密相關(guān)的,有持續(xù)化的要求。目前在商業(yè)領(lǐng)域和開源世界,都有一些優(yōu)秀的工具可以為Model層的開發(fā)提供便利。
1.2 業(yè)務(wù)邏輯層框架Spring Spring是一個(gè)解決了許多J2EE開發(fā)中常見問題并能夠替代EJB技術(shù)的強(qiáng)大的輕量級框架。這里所說的輕量級指的是Spring框架本身,而不是指Spring只能用于輕量級的應(yīng)用開發(fā)。Spring的輕盈體現(xiàn)在其框架本身的基礎(chǔ)結(jié)構(gòu)以及對其他應(yīng)用工具的支持和裝配能力。與EJB這種龐然大物相比,Spring可使程序研發(fā)人員把各個(gè)技術(shù)層次之間的風(fēng)險(xiǎn)降低。
Spring框架的核心是控制翻轉(zhuǎn)IoC(Inversion of Control)/依賴注入DI(Dependence Injection)機(jī)制。IoC是指由容器中控制組件之間的關(guān)系(這里,容器是指為組件提供特定服務(wù)和技術(shù)支持的一個(gè)標(biāo)準(zhǔn)化的運(yùn)行時(shí)的環(huán)境)而非傳統(tǒng)實(shí)現(xiàn)中由程序代碼直接操控,這種將控制權(quán)由程序代碼到外部容器的轉(zhuǎn)移,稱為“翻轉(zhuǎn)”。DI是對IoC更形象的解釋,即由容器在運(yùn)行期間動態(tài)地將依賴關(guān)系(如構(gòu)造參數(shù)、構(gòu)造對象或接口)注入到組件之中。Spring采用設(shè)值注入(使用Setter方法實(shí)現(xiàn)依賴)和構(gòu)造子注入(在構(gòu)造方法中實(shí)現(xiàn)依賴)的機(jī)制,通過配置文件管理組建的協(xié)作對象,創(chuàng)建可以構(gòu)造組件的IoC容器。這樣,不需要編寫工廠模式、單例模式或者其他構(gòu)造的方法,就可以通過容器直接獲取所需的業(yè)務(wù)組件。Spring框架的結(jié)構(gòu)如圖2所示。
[3]
[3]
Spring框架由七個(gè)定義明確的模塊組成,且每個(gè)模塊或組件都可以單獨(dú)存在,或者與其他一個(gè)或多個(gè)模塊聯(lián)合實(shí)現(xiàn)。Spring Core Container是一個(gè)用來管理業(yè)務(wù)組件的IoC容器,是Spring應(yīng)用的核心;Spring DAO和Spring ORM不僅提供數(shù)據(jù)訪問的抽象模塊,還集成了對Hibernate、JDO和iBatis等流行的對象關(guān)系映射框架的支持模塊,并且提供了緩沖連接池、事務(wù)處理等重要的服務(wù)功能,保證了系統(tǒng)的性能和數(shù)據(jù)的完整性;Sprnig Web模塊提供了Web應(yīng)用的一些抽象封裝,可以將Struts、Webwork等Web框架與Spring整合成為適用于自己的解決方案。
Spring框架可以成為企業(yè)級應(yīng)用程序一站式的解決方案,同時(shí)它也是模塊化的框架,允許開發(fā)人員自由地挑選適合自己應(yīng)用的模塊進(jìn)行開發(fā)。Spring框架式是一個(gè)松耦合的框架,框架的部分耦合度被設(shè)計(jì)為最小,在各個(gè)層次上具體選用哪個(gè)框架取決于開發(fā)者的需要。1.3 數(shù)據(jù)持久層框架Hibernate O/R mapping技術(shù)是為了解決關(guān)系型數(shù)據(jù)庫和面向?qū)ο蟮某绦蛟O(shè)計(jì)之間不匹配的矛盾而產(chǎn)生的。Hibernate是目前最為流行的O/R mapping框架,它在關(guān)系型數(shù)據(jù)庫和Java對象之間做了一個(gè)自動映射,使得程序員可以以非常簡單的方式實(shí)現(xiàn)對數(shù)據(jù)庫的操作。Hibernate工作原理如圖3所示。
Hibernate通過對JDBC的封裝,向程序員屏蔽了底層的數(shù)據(jù)庫操作,使程序員專注于OO程序的開發(fā),有助于提高開發(fā)效率。程序員訪問數(shù)據(jù)庫所需要做的就是為持久化對象編制xml映射文件。
底層數(shù)據(jù)庫的改變只需要簡單地更改初始化配置文件(hibernate.cfg.xml或者h(yuǎn)ibernate.properties)即可,不會對應(yīng)用程序產(chǎn)生影響。
Hibernate有自己的面向?qū)ο蟮牟樵冋Z言HQL,HQL功能強(qiáng)大,支持目前大部分主流的數(shù)據(jù)庫,如Oracle、DB2、MySQL、Microsoft SQL Server等,是目前應(yīng)用最廣泛的O/R映射工具。Hibernate為快速開發(fā)應(yīng)用程序提供了底層的支持。2 基于SSH組合框架的Web應(yīng)用模型設(shè)計(jì)與實(shí)現(xiàn) 2.1 集成SSH的新型J2EE框架
前面分析了基于J2EE的三種框架技術(shù),下面通過集成以上三種框架技術(shù)來對傳統(tǒng)的J2EE Web開發(fā)模型加以改進(jìn),以形成一種新的、輕量型的J2EE架構(gòu)。
集成SSH框架的系統(tǒng)框架圖如圖4所示,系統(tǒng)從職責(zé)上分為四層:表示層、業(yè)務(wù)邏輯層、[4]數(shù)據(jù)持久層和域模塊層。其中使用Struts作為系統(tǒng)的整體基礎(chǔ)架構(gòu),負(fù)責(zé)MVC的分離,在Struts框架的模型部分,利用Hibernate框架對持久層提供支持,業(yè)務(wù)層用Spring支持。具體做法是:用面向?qū)ο蟮姆治龇椒ǜ鶕?jù)需求提出一些模型,將這些模型實(shí)現(xiàn)為基本的Java對象,然后編寫基本的DAO接口,并給出Hibernate的DAO實(shí)現(xiàn),采用Hibernate架構(gòu)實(shí)現(xiàn)的DAO類來實(shí)現(xiàn)Java類與數(shù)據(jù)庫之間的轉(zhuǎn)換和訪問,最后由Spring完成業(yè)務(wù)邏輯。
系統(tǒng)的基本業(yè)務(wù)流程是: 在表示層中,首先通過JSP頁面實(shí)現(xiàn)交互界面,負(fù)責(zé)傳送請求(Request)和接收響應(yīng)(Response),然后Struts根據(jù)配置文件(struts-config.xml)將ActionServlet接收到的Request委派給相應(yīng)的Action處理。在業(yè)務(wù)層中,管理服務(wù)組件的Spring IoC容器負(fù)責(zé)向Action提供業(yè)務(wù)模型(Model)組件和該組件的協(xié)作對象數(shù)據(jù)處理(DAO)組件完成業(yè)務(wù)邏輯,并提供事務(wù)處理、緩沖池等容器組件以提升系統(tǒng)性能和保證數(shù)據(jù)的完整性。而在持久層中,則依賴于Hibernate的對象化映射和數(shù)據(jù)庫交互,處理DAO組件請求的數(shù)據(jù),并返回處理結(jié)果。
采用上述開發(fā)模型,不僅實(shí)現(xiàn)了視圖、控制器與模型的徹底分離,而且還實(shí)現(xiàn)了業(yè)務(wù)邏輯層與持久層的分離。這樣無論前端如何變化,模型層只需很少的改動,并且數(shù)據(jù)庫的變化也不會對前端有所影響,大大提高了系統(tǒng)的可復(fù)用性。而且由于不同層之間耦合度小,有利于團(tuán)隊(duì)成員并行工作,大大提高了開發(fā)效率。
2.2 基于SSH框架的Web應(yīng)用系統(tǒng)的實(shí)現(xiàn)
下面將通過一個(gè)實(shí)際的系統(tǒng)來展示如何進(jìn)行基于SSH框架的Web應(yīng)用開發(fā)。該系統(tǒng)是為某通信公司運(yùn)營部開發(fā)的一個(gè)問答式系統(tǒng),功能類似于百度知道和新浪愛問。由于系統(tǒng)的模塊較多,下面就以一個(gè)用戶管理模塊為例來說明系統(tǒng)的開發(fā)實(shí)現(xiàn)過程,并將按照數(shù)據(jù)持久層、業(yè)務(wù)邏輯層、表示層的順序說明系統(tǒng)構(gòu)建過程。
(1)數(shù)據(jù)持久層
數(shù)據(jù)持久層由Java對象持久化類和數(shù)據(jù)訪問對象(DAO)組成。每個(gè)數(shù)據(jù)庫表都對應(yīng)著一個(gè)持久化對象,這樣就給予了開發(fā)者使用OO思想設(shè)計(jì)和開發(fā)的便利,同時(shí)也屏蔽了具體的數(shù)據(jù)庫和具體的數(shù)據(jù)表、字段,消除了對數(shù)據(jù)庫操作的硬編碼在重用性上的弊端。用戶信息表的部分結(jié)構(gòu)如表1所示。
Hibernate通過映射(Mapping)文件將對象(Object)與關(guān)系型數(shù)據(jù)(Relational)相關(guān)聯(lián),因此需要編寫和數(shù)據(jù)庫表相對應(yīng)的Java持久化類以及對應(yīng)的映射文件。有了Java持久化類后就可以在此基礎(chǔ)上實(shí)現(xiàn)數(shù)據(jù)訪問類。在Spring框架中,數(shù)據(jù)訪問類可以從輔助類HibernateDaoSupport繼承,這極大地方便了Hibernate框架在Spring中的使用,相應(yīng)的部分代碼如下:
public class UserDao extends HibernateDaoSupport { public int add(User user){ return Integer.ParseInt(this.getHibernateTemplate().save(user).toString());} public List findAll(){ return this.getHibernateTemplate().loadAll(User.class);} }
具體的Hibernate數(shù)據(jù)源、session工廠、事務(wù)管理、緩沖連接池等功能都由業(yè)務(wù)層的Spring容器提供。
(2)業(yè)務(wù)邏輯層
業(yè)務(wù)邏輯層由Spring框架支持,提供了處理業(yè)務(wù)邏輯的服務(wù)組件。開發(fā)者需要對業(yè)務(wù)對象建模,抽象出業(yè)務(wù)模型并封裝在Model組件中。由于數(shù)據(jù)持久層實(shí)現(xiàn)了Java持久化類并且封裝了數(shù)據(jù)訪問對象(DAO),因此可以在Model組件中方便地調(diào)用DAO組件來存取數(shù)據(jù)。Spring的IoC容器負(fù)責(zé)統(tǒng)一管理Model組件和DAO組件以及Spring所提供的事務(wù)處理、緩沖連接池等服務(wù)組件。
在用戶管理模塊中,通過業(yè)務(wù)建模創(chuàng)建了用戶模型UserService類,封裝了對用戶的權(quán)限管理以及積分管理等功能。UserService類通過調(diào)用數(shù)據(jù)訪問類UserDao實(shí)現(xiàn)對用戶數(shù)據(jù)的操作。這些組件的關(guān)系將通過配置Spring框架的applicationContext.xml聯(lián)系起來,配置文件的主要內(nèi)容如下:
(3)表示層
表示層結(jié)合JSP和Struts的TagLib庫處理顯示功能,利用ActionServlet將請求(*.do)映射到相應(yīng)的Action,并由Action調(diào)用業(yè)務(wù)邏輯的服務(wù)組件,然后根據(jù)處理結(jié)果跳轉(zhuǎn)到Forword對象指定的響應(yīng)頁面。
業(yè)務(wù)流程的部署由struts-config.xml完成。下面以一個(gè)顯示所有用戶信息的請求(ListUser.do)為例來說明配置文件的使用。
基于J2EE的Web應(yīng)用以其層次性、平臺無關(guān)性的優(yōu)勢已經(jīng)逐漸成為了電子商務(wù)、電子政務(wù)主要的解決方案。本文針對傳統(tǒng)的J2EE Web應(yīng)用開發(fā)的弊端,提出了一種利用輕量級框架來快速搭建Web應(yīng)用的解決方案,并且通過其在實(shí)際項(xiàng)目中的應(yīng)用,證明了采用此方案可以幫助開發(fā)人員在短時(shí)間內(nèi)建立結(jié)構(gòu)清晰、可重用性好、維護(hù)擴(kuò)展方便的Web應(yīng)用程序。參考文獻(xiàn)
[1] GAMMA E, HELM R, JOHNSON R, et al.Design patterns:Elements of reusable object-oriented software[M].Addison Wesley, 1994.[2] 孫衛(wèi)琴.精通Struts:基于MVC的Java Web設(shè)計(jì)與開發(fā)[M].北京:電子工業(yè)出版社,2004.[3] JOHNSON R, HOELLER J, ARENDSEN A, et al.Java/J2EE application framework reference document.V1.1.2004.[4] 徐長盛,戴超.一種快速開發(fā)Web應(yīng)用程序方法的研究[J].計(jì)算機(jī)工程與設(shè)計(jì),2004,(12):2237-2239.[5] 夏昕,曹曉鋼,唐勇.深入淺出Hibernate[M].北京:電子工業(yè)出版社,2005.[6] JOHNSON R.Expert one-on-one J2EE design and development[M].魏海萍譯.北京:電子工業(yè)出版社,2003.在用ssh開發(fā)web應(yīng)用時(shí),需要對生成的各個(gè)類文件進(jìn)行組織,下面就對一個(gè)可行的目錄方案進(jìn)行介紹:
譬如應(yīng)用中有一個(gè)用戶管理模塊,則在公共包下建立一個(gè)user包,如該公共包可以為com.simon.oa,在user包下包括如下子包
1、controler包
該包放置各種struts的action。
2、dao包
該包放置各類dao(data access object),也就是放置對數(shù)據(jù)庫訪問的實(shí)現(xiàn)類,在用myeclipse中的“Hibernate Reverse Engineering”進(jìn)行反向操作時(shí)在某一個(gè)目錄中就會生成對應(yīng)某個(gè)表的DAO,生成后可將該DAO拖到dao包中。在某些應(yīng)用中將DAO作為接口,在該接口中包括所有對數(shù)據(jù)庫的操作方法,然后在dao包建立一個(gè)hibernate包,在hibernate包中放置對DAO接口的實(shí)現(xiàn),譬如:UserDAO接口有一個(gè)實(shí)現(xiàn)類為UserDaoImpl,將該類放置到hibernate包中,實(shí)際的開發(fā)傾向于后一種方式,因?yàn)閷@個(gè)DAO接口可以實(shí)現(xiàn)spring的IoC操作。(不知道m(xù)yeclipse對此是怎么考慮的,這個(gè)問題讓我糾纏了很久,誤將DAO理解成一個(gè)能夠進(jìn)行實(shí)際操作的類,而不是一個(gè)接口,以后開發(fā)要注意)
3、model包
該包中放置hibernate反向工程生成的bean和該bean對應(yīng)的.hbm.xml文件。
4、service包 該包放置業(yè)務(wù)操作類,譬如用戶服務(wù)類,一般情況將該用戶操作類提取一個(gè)接口,然后在service包下生成一個(gè)impl包,在impl包中才放置用戶操作接口的實(shí)現(xiàn)類。該用戶接口實(shí)現(xiàn)類中調(diào)用DAO接口對數(shù)據(jù)庫進(jìn)行操作,而調(diào)用該實(shí)現(xiàn)類的方法在struts的action中。
5、vo包(value object)
vo包中的中包括struts中使用的POJO及actionform等信息。VO: Value Object DTO: Data Transfer Object 個(gè)人理解VO和DTO是類似的東西,原則上VO和DTO只有Public Fields,主要用于進(jìn)程之間數(shù)據(jù)傳遞的問題,VO和DTO不會傳遞到表示層,在業(yè)務(wù)層就會被吸收。但看到很多人在建立VO和DTO時(shí),也含有Setter,Getter屬性和一些其它的輔助方法,這也無可厚非,我自己也不能確定這對不對。posted
第二篇:java的SSH框架總結(jié)(范文)
對于java中SSH框架的理解
SSH 是指 Struts+ spring+ hibernate 的一個(gè)集成框架,這是一種比較流行的java web應(yīng)用程序開源框架。
Struts
Struts是一個(gè)基于Sun J2EE平臺的MVC框架,主要采用Servlet和JSP技術(shù)來實(shí)現(xiàn)的。由于Struts能充分滿足引用開發(fā)的需求,簡單易用,敏捷迅速,所以很受關(guān)注。Struts 吧Servlet、JSP、自定義標(biāo)簽和信息資源(message resource)整合到一個(gè)統(tǒng)一的框架中,開發(fā)人員利用其進(jìn)行開發(fā)室不用再自己編寫實(shí)現(xiàn)全套的MVC模式,極大的節(jié)省了時(shí)間。
Spring Spring是一個(gè)解決了許多在J2EE開發(fā)中常見的問題的強(qiáng)大框架。Spring提供了管理業(yè)務(wù)對象的一致方法并且鼓勵(lì)了注入對接口編程而不是對類編程的良好習(xí)慣。
Spring的架構(gòu)基礎(chǔ)是基于JavaBean屬性的Inversion of Control 容器。然而,這僅僅是完整圖景中的一部分:在Spring使用IOC容器作為構(gòu)建完關(guān)注所有架構(gòu)層的完整解決方案是獨(dú)一無二的。Spring提供了唯一的數(shù)據(jù)訪問抽象,包括簡單和有效率的JDBC框架,極大的改進(jìn)了效率并且減少了可能的錯(cuò)誤。Spring的數(shù)據(jù)訪問架構(gòu)還集成了Hibernate 和其他O/R mapping 解決方案。Spring還提供了唯一的事物管理抽象。它能夠在各種底層事務(wù)管理技術(shù),例如JTA 或者JDBC事務(wù)提供一個(gè)一致的編程模型。Spring提供了一個(gè)標(biāo)準(zhǔn)Java語言編寫的AOP框架,他給POJOs提供了聲明式的事務(wù)管理和其他企業(yè)事務(wù)——如果有必要還可以實(shí)現(xiàn)自己的aspects。這個(gè)框架提供了可以和IOC容器集成的強(qiáng)大而靈活的MVC web框架。
Hibernate Hibernate 是一個(gè)開放源代碼的對象關(guān)系映射框架,它對JDBC進(jìn)行了非常輕量級的對象封裝,是的java程序員可以隨心所欲的適用對象編程思維來操控?cái)?shù)據(jù)庫。Hibernate 可以應(yīng)用在任何使用JDBC的場合,既可以在java客戶端程序中使用,也可以在Servlet/jsp的web應(yīng)用中使用,其最具革命性意義的是:Hibernate可以在應(yīng)用EJB的J2EE架構(gòu)中取代CMP,完成數(shù)據(jù)持久化的重任。
在SSH的組合框架模式中,三者各自的作用 Struts 是一個(gè)很好的MVC框架,主要技術(shù)是Servlet和JSP。Struts的MVC設(shè)計(jì)模式可以讓我們的邏輯思維變得很清晰,讓我們寫程序?qū)哟畏置鳌?/p>
Spring提供了管理業(yè)務(wù)對象的一致方法,并鼓勵(lì)注入對接口編程而不是對類編程的良好習(xí)慣,使我們的產(chǎn)品在最大程度上解耦。
Hibernate 是用來持久化數(shù)據(jù)的,提供了完全面向?qū)ο蟮臄?shù)據(jù)庫操作。Hibernate對JDBC進(jìn)行了非常輕量級的封裝,使得他與關(guān)系型數(shù)據(jù)庫打交道變得非常輕松。
負(fù)責(zé)Web層:
ActionFormBean接收網(wǎng)頁中表單提交的數(shù)據(jù),然后通過Action進(jìn)行處理,再Forward到對應(yīng)的網(wǎng)頁,在Struts-config.xml中定義了
Spring負(fù)責(zé)業(yè)務(wù)層管理,即Service:
Service為Action提供統(tǒng)一的調(diào)用接口,封裝持久層的DAO,并集成Hibernate,Spring可對JavaBean和事物進(jìn)行統(tǒng)一管理。
Hibernate負(fù)責(zé)持久層,完成數(shù)據(jù)庫的CRUD操作:
Hibernate有一組hbm.xml文件和PO,是與數(shù)據(jù)庫中的表相對應(yīng)的,然后定義DAO,這些是與數(shù)據(jù)庫打交道的類。
在Struts+Spring+Hibernate系統(tǒng)中,對象之間的調(diào)用流程如下:
Struts——>Spring——>Hibernate
JSP——>Action——>Service——>DAO——>Hibernate
第三篇:基于SSH框架的學(xué)生檔案信息管理系統(tǒng)
畢業(yè)設(shè)計(jì)(論文)
基于SSH框架的學(xué)生信息管理系統(tǒng)
系
別 : 計(jì)算機(jī)科學(xué)與技術(shù)系
專業(yè)(班級): 計(jì)算機(jī)科學(xué)與技術(shù)(2011級1班)作者(學(xué)號): 指導(dǎo)教師: 完成日期:
陳洪(51102011007)
顧珺、李鐵柱
2015年4月15日
蚌埠學(xué)院教務(wù)處制
目錄
摘 要........................................................................................................................Abstract....................................................................................................................1 概 述..................................................................................................................1.1 項(xiàng)目簡介............................................................................................................1.1.1 研究背景.........................................................................................................1.1.2 項(xiàng)目開發(fā)目的.................................................................................................1.1.3 項(xiàng)目開發(fā)的意義.............................................................................................2 開發(fā)工具及相關(guān)簡介........................................................................................2.1 SSH框架簡介....................................................................................................2.2 Jsp技術(shù)簡介......................................................................................................2.3 MySQL數(shù)據(jù)庫簡介..........................................................................................2.4 Tomcat 6.0介紹..................................................................................................2.5 MyEclipse的簡介..............................................................................................3 可行性分析........................................................................................................3.1 技術(shù)可行性........................................................................................................3.2 經(jīng)濟(jì)可行性........................................................................................................3.3 社會可行性........................................................................................................3.3.1 法律因素.........................................................................................................3.3.2 用戶使用可行性.............................................................................................4 需求分析............................................................................................................4.1 功能分析............................................................................................................4.1.1 管理員模塊.....................................................................................................4.1.2 教師模塊.........................................................................................................4.1.3 學(xué)生模塊.........................................................................................................4.2 系統(tǒng)分析............................................................................................................4.2.1 業(yè)務(wù)流程分析.................................................................................................4.2.2 功能流程分析...............................................................................................5 總體設(shè)計(jì)..........................................................................................................5.1 系統(tǒng)功能設(shè)計(jì)..................................................................................................5.2 數(shù)據(jù)庫設(shè)計(jì)....................................................................................................5.2.1 概念結(jié)構(gòu)設(shè)計(jì)...............................................................................................5.2.2 管理員實(shí)體屬性圖.......................................................................................5.2.3 教師實(shí)體屬性圖...........................................................................................5.2.4 學(xué)生實(shí)體屬性圖...........................................................................................5.2.5 公告實(shí)體屬性圖...........................................................................................5.2.6 成績實(shí)體屬性圖...........................................................................................5.2.7 E-R模型的組成元素....................................................................................5.3 數(shù)據(jù)庫表設(shè)計(jì)..................................................................................................5.3.1 管理員信息表...............................................................................................5.3.2 教師信息表...................................................................................................5.3.3 學(xué)生信息表...................................................................................................5.3.4 成績信息表...................................................................................................5.3.5 公告信息表...................................................................................................5.4 系統(tǒng)開發(fā)工具與開發(fā)模式的選擇..................................................................5.4.1 系統(tǒng)開發(fā)工具...............................................................................................5.4.2 系統(tǒng)設(shè)計(jì)模式...............................................................................................6 詳細(xì)設(shè)計(jì)............................................................................................................6.1 系統(tǒng)登錄模塊..................................................................................................6.1.1 登錄模塊.......................................................................................................6.2.1 基礎(chǔ)信息管理界面.......................................................................................6.2.2 個(gè)人信息管理界面.......................................................................................6.2.3 課程管理界面...............................................................................................6.2.4 錄入教師信息界面.......................................................................................6.2.5 錄入學(xué)生信息界面.......................................................................................6.3 教師用戶模塊..................................................................................................6.3.1 個(gè)人信息管理界面.......................................................................................6.3.2 查看公告信息界面.......................................................................................6.3.3 學(xué)生成績管理界面.......................................................................................6.4 學(xué)生登錄模塊..................................................................................................6.4.1 查看個(gè)人信息界面.......................................................................................6.4.2 課程查詢界面...............................................................................................6.4.3 學(xué)校公告界面...............................................................................................6.4.4 我的成績界面...............................................................................................7 軟件測試與分析..............................................................................................7.1 軟件測試的重要性..........................................................................................7.2 系統(tǒng)的實(shí)際測試..............................................................................................7.3 測試環(huán)境與測試條件......................................................................................7.4 系統(tǒng)運(yùn)行情況..................................................................................................8 結(jié)
論..............................................................................................................8.1 系統(tǒng)的優(yōu)勢......................................................................................................8.2 系統(tǒng)的不足......................................................................................................8.3 總結(jié)..................................................................................................................致
謝....................................................................................................................參 考 文 獻(xiàn)..........................................................................................................蚌埠學(xué)院畢業(yè)設(shè)計(jì)(論文)
基于SSH框架的學(xué)生信息檔案管理系統(tǒng)
摘要:由于現(xiàn)在高校招生率的提高,各大高校的學(xué)生數(shù)量普遍增多,隨之增加的就是學(xué)生檔案信息。面對如今繁雜龐大的信息數(shù)據(jù)庫,傳統(tǒng)的手工記錄的管理方式顯得力不從心。伴隨著網(wǎng)絡(luò)信息技術(shù)在教育事業(yè)上的應(yīng)用,學(xué)生檔案管理相應(yīng)而出,使得學(xué)生檔案的管理比以前更方便。本文就此系統(tǒng)做了全面的解析概述,以及每個(gè)功能所應(yīng)用的核心技術(shù)及其運(yùn)用方法,整體結(jié)構(gòu)方法也做了全面介紹。本系統(tǒng)以Myeclipse 2014做為開發(fā)工具,用作支持本系統(tǒng)的數(shù)據(jù)庫是MySQL,服務(wù)器選擇的是Tomcat 6.0,Jsp和Servlet技術(shù)用作前臺開發(fā)手段[1],總體框架采用SSH架構(gòu)。項(xiàng)目總體分為前、后臺兩個(gè)部分,前臺提供管理員、教師用戶和學(xué)生用戶的登錄。教師用戶的主要職責(zé)是評定學(xué)生的考試成績,此外還可以對個(gè)人信息和公告信息進(jìn)行查看;學(xué)生用戶可以查看個(gè)人信息、課程信息、公告信息和個(gè)人成績;管理員則從后臺登錄,管理數(shù)據(jù)庫中的相關(guān)信息如對學(xué)生用戶和教師用戶的添加,以及對公告信息的調(diào)整。
關(guān)鍵詞:SSH框架;MyEclipse;MySQL;Tomcat;Jsp
陳洪:基于SSH框架的學(xué)生檔案信息管理
Students Information File Management System Based on SSH Framework Abstract: Now that increase college enrollment rates, the number of students in major universities generally increased, increased is the student profile information.Today the face of huge information database complexity, the traditional manual records management appeared to be inadequate.Along with the network of information technology, student records management in the education of corresponding out, making the management of student records and more convenient than ever.In this paper, this system made a comprehensive analysis overview, and each function is applied the core technology and its application method, the overall structure of methods to do a comprehensive introduction.This system Myeclipse 2014 as a development tool, used to support the system's database is MySQL, the server is selected Tomcat 6.0, Jsp and Servlet technologies used as foreground development tools, the overall framework architecture using SSH.The overall project is divided into front, back two parts, the front desk manager, teacher and student user to provide the user's login.Teachers users to view personal information, student information and post information, modify personal information, as well as courses for student achievement rates;student users can view personal information, course information, announcements and personal achievement;background provide administrator login, database management for the information of student, teacher and so on[2].Keywords: SSH Framework;MyEclipse;MySQL;Tomcat;Jsp
蚌埠學(xué)院畢業(yè)設(shè)計(jì)(論文)開發(fā)工具及相關(guān)簡介
2.1 SSH框架簡介
SSH框架包括了Struts、Spring以及Hibernate,是目前使用相對廣泛的一個(gè)Web開源架構(gòu)[3]。
SSH框架結(jié)構(gòu)清晰,大體可分為表示層、業(yè)務(wù)邏輯層、數(shù)據(jù)持久層和域模塊層,各層之間有很好的關(guān)聯(lián)。該框架有諸多的優(yōu)點(diǎn),如有較強(qiáng)的可復(fù)用性,能夠幫助使用者更為清晰的搭建需要使用的架構(gòu),而且可維護(hù)性好。本系統(tǒng)中的Struts框架采用的是Struts2,主要用在系統(tǒng)的業(yè)務(wù)邏輯層,負(fù)責(zé)業(yè)務(wù)的跳轉(zhuǎn);Hibernate框架主要是負(fù)責(zé)對數(shù)據(jù)庫進(jìn)行輕量級封裝,做持久化處理,可以減少SQL語句的編寫;Spring框架則是Struts和Hibernate框架的管理者[4]。SSH框架的實(shí)現(xiàn)方法是通過配置相關(guān)的xml文件,然后導(dǎo)入一些所需要的jar包,然后對這些jar包進(jìn)行調(diào)用。
2.2 Jsp技術(shù)簡介
Jsp是SUN公司和許多公司一起建立的一種動態(tài)技術(shù)標(biāo)準(zhǔn)。Jsp網(wǎng)頁的組成其實(shí)是在原本網(wǎng)頁中的Html文件中加入了Jsp標(biāo)簽和相關(guān)Java程序片段從而形成了如今Jsp網(wǎng)頁[5]。Jsp技術(shù)的最大特點(diǎn)就是它的所有操作都能在服務(wù)器端的瀏覽器上進(jìn)行,然后將運(yùn)行的結(jié)果反饋給用戶,這樣就會很大程度降低對用戶的客戶端瀏覽器的要求。
Jsp技術(shù)是Java Servlet API的擴(kuò)展。Jsp頁面通常由靜態(tài)Html/XML組件、自定義Jsp標(biāo)簽和成為scriptlet的Java代碼片段組成。Jsp技術(shù)是建立在Servlet基礎(chǔ)上的,兩者的執(zhí)行都是在服務(wù)器端的,用戶可以通過瀏覽器直接進(jìn)行訪問執(zhí)行。當(dāng)服務(wù)器端出現(xiàn)訪問請求超時(shí)時(shí),其中的Java程序會首先被執(zhí)行,之后將執(zhí)行結(jié)果返回給用戶,一并返回的還有相關(guān)的Html代碼。通常的情況下Jsp頁面很少去操作后臺的數(shù)據(jù),Jsp頁面只是用來提取輸入到網(wǎng)頁上的數(shù)據(jù)和解決網(wǎng)頁的靜態(tài)化頁面,至于業(yè)務(wù)處理則毫無干系。若想要對數(shù)據(jù)庫進(jìn)行操作或者對網(wǎng)頁重定向以及發(fā)送E-mail,都是通過向網(wǎng)頁中插入Java代碼來實(shí)現(xiàn),其它所需功能也都可以通過此方法實(shí)現(xiàn)。
陳洪:基于SSH框架的學(xué)生檔案信息管理
2.3 MySQL數(shù)據(jù)庫簡介
MySQL的開發(fā)者是瑞典的MySQL AB公司,它是一個(gè)免費(fèi)開源的數(shù)據(jù)庫,對于Windows、Linux等操作系統(tǒng)都適用;此外,MySQL對于網(wǎng)絡(luò)也是完全適用的,也就是說用其構(gòu)建的數(shù)據(jù)庫可以被網(wǎng)上的任何服務(wù)器進(jìn)行訪問[6]。MySQL具有十分強(qiáng)大的功能,操作又十分簡單,而且有較強(qiáng)的安全可靠性,運(yùn)行速度方面也不會讓人失望,也比較容易管理。除了以上特點(diǎn),MySQL最大的優(yōu)點(diǎn)就是它是一個(gè)免費(fèi)開源軟件!而且能夠直接從網(wǎng)上下載到與其相匹配的第三方工具軟件對其操作。在諸如Unix這樣的操作系統(tǒng)上MySQL可以直接下載它的服務(wù)器和客戶端軟件。如果是在Windows系統(tǒng)上,MySQL數(shù)據(jù)庫的客戶機(jī)和客戶機(jī)程序也都是免費(fèi)的,這就是MySQL最大的優(yōu)勢。
2.4 Tomcat 6.0介紹
Tomcat服務(wù)器是由Apache Software Foundation(Apache軟件基金會)中Apache Group Jadarta開發(fā)小組開發(fā)的一個(gè)免費(fèi)的開放源代碼的服務(wù)器軟件[7]。Tomcat能夠完全兼容Jsp和Servlet的最新規(guī)范,這都得力于SUN公司的支持。它的不足之處就是它的配置比較繁瑣,而且還具有一些安全問題,但是這些缺點(diǎn)絲毫不會影響它在開發(fā)者心中的地位,Tomcat先進(jìn)的技術(shù),出色的穩(wěn)定性和兼容性,以及最為人性化的免費(fèi)政策都是深受開發(fā)者喜愛的原因,成為應(yīng)用最為廣泛的WEB應(yīng)用服務(wù)器軟件。
2.5 MyEclipse的簡介
MyEclipse是一個(gè)企業(yè)級的工作平臺,它的前生只是Eclipse的一個(gè)插件[8],但是如今卻發(fā)展成為比Eclipse功能更為強(qiáng)大的工作平臺。它是Eclipse的擴(kuò)展,對Eclipse的功能有全面性的完善,其中就包括了完備的編碼,調(diào)試、測試和發(fā)布功能。利用它可以在數(shù)據(jù)庫和JavaEE(Java Enterpride Edition)的開發(fā)、發(fā)布以及應(yīng)用程序服務(wù)器的整合方面大大提高了工作效率,能豐富JavaEE集成開發(fā)環(huán)境。
在MyEclipse組件中,更為方便地提供了Jsp、Servlet、HTML、XML、Struts、Hibernate、Spring等框架的開發(fā)支持,而隨著不斷衍生發(fā)展的新技術(shù),MyEclipse也在不斷的發(fā)展[9]。
蚌埠學(xué)院畢業(yè)設(shè)計(jì)(論文)需求分析
4.1 功能分析
4.1.1 管理員模塊
系統(tǒng)管理員可以修改個(gè)人密碼,添加修改課程信息、系別信息以及公告信息,此外還可以瀏覽學(xué)生基本信息和成績。
4.1.2 教師模塊
本系統(tǒng)中的教師用戶的主要職責(zé)是對學(xué)生成績進(jìn)行評定,給出相應(yīng)分?jǐn)?shù),然后可以根據(jù)課程信息和考試時(shí)間對考試成績查看。另外,可以對相關(guān)信息進(jìn)行查看,如個(gè)人信息、公告信息和學(xué)生信息等。
4.1.3 學(xué)生模塊
學(xué)生模塊實(shí)現(xiàn)的有查看個(gè)人信息、課程信息、考試成績和公告信息,此外可以添加自己的相關(guān)課程。
4.2 系統(tǒng)分析
4.2.1 業(yè)務(wù)流程分析
所謂業(yè)務(wù)流程分析是指在進(jìn)行設(shè)計(jì)開發(fā)之前,對自己的系統(tǒng)設(shè)計(jì)按照所給要求進(jìn)行相關(guān)的業(yè)務(wù)處理,設(shè)計(jì)好系統(tǒng)的總體框架,根據(jù)實(shí)際功能需要進(jìn)行相關(guān)的業(yè)務(wù)分析,對各個(gè)業(yè)務(wù)流程進(jìn)行詳細(xì)分析,如業(yè)務(wù)內(nèi)容、處理所需的大致時(shí)間等,此外對各個(gè)業(yè)務(wù)流程所需的信息來源、信息處理的方法也都要有相對應(yīng)的總結(jié),這樣才能更好的進(jìn)行開發(fā)。
業(yè)務(wù)流程分析就是為了讓開發(fā)者能夠更好的了解系統(tǒng)的開發(fā)方向以及相關(guān)的流程處理,幫助開發(fā)者梳理清楚開發(fā)思路,使系統(tǒng)開發(fā)更有條理。一下是系統(tǒng)的流程圖。
具體業(yè)務(wù)流程如圖4-1所示。
陳洪:基于SSH框架的學(xué)生檔案信息管理
學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)學(xué)
圖 4-1 業(yè)務(wù)流程圖
4.2.2 系統(tǒng)功能流程分析
功能流程分析能夠讓開發(fā)者對自己的系統(tǒng)中各個(gè)角色的功能之間的關(guān)系更加明了,這讓系統(tǒng)更加結(jié)構(gòu)化,在開發(fā)的同時(shí)自己在大腦中能夠有一個(gè)明確的構(gòu)思,有一個(gè)清晰的開發(fā)方向。
下面將具體分析學(xué)生角色和管理員之間的關(guān)系,因?yàn)榻處熍c管理員之間的關(guān)系與此基本相似,所以就不一一列出。
具體功能流程圖如圖4-2所示:
0-
陳洪:基于SSH框架的學(xué)生檔案信息管理 總體設(shè)計(jì)
5.1 系統(tǒng)功能設(shè)計(jì)
系統(tǒng)設(shè)計(jì)是一個(gè)把功能需求轉(zhuǎn)換成用設(shè)計(jì)程序表示的過程。系統(tǒng)設(shè)計(jì)時(shí)需要事先對要實(shí)現(xiàn)的目標(biāo)系統(tǒng)進(jìn)行分析研究,之后總結(jié)出一套系統(tǒng)的設(shè)計(jì)規(guī)劃,以便能夠有計(jì)劃地設(shè)計(jì)系統(tǒng)。對于本系統(tǒng)來說,設(shè)計(jì)之前就要對學(xué)生檔案信息管理系統(tǒng)進(jìn)行全方位的調(diào)查總結(jié),然后繪制一個(gè)系統(tǒng)的總體規(guī)劃。系統(tǒng)設(shè)計(jì)包括系統(tǒng)總體設(shè)計(jì)、系統(tǒng)詳細(xì)設(shè)計(jì)、系統(tǒng)數(shù)據(jù)庫設(shè)計(jì)、系統(tǒng)開發(fā)工具、開發(fā)技術(shù)和開發(fā)模式等[8]。
從數(shù)據(jù)流圖出發(fā),對數(shù)據(jù)流圖進(jìn)行分析,如圖5-1:
后臺功能模塊老師模塊管理員模塊學(xué)生模塊個(gè)人信息學(xué)生成績系統(tǒng)公共個(gè)人信息學(xué)院管理系別管理班級管理課程管理班級管理學(xué)生管理公共管理個(gè)人信息我的成績系統(tǒng)公共
圖5-1 學(xué)生信息管理系統(tǒng)功能模塊圖
5.2 數(shù)據(jù)庫設(shè)計(jì)
本系統(tǒng)主的功能實(shí)現(xiàn)主要圍繞數(shù)據(jù)庫展開的,所以本系統(tǒng)的核心就是數(shù)據(jù)庫。數(shù)據(jù)庫的設(shè)計(jì)直接影響本設(shè)計(jì)各項(xiàng)功能的進(jìn)行,是本設(shè)計(jì)的基礎(chǔ),就像是房子的根基一樣,它的穩(wěn)固與否直接影響整個(gè)建筑的施工,所以在設(shè)計(jì)本系統(tǒng)的數(shù)據(jù)庫的時(shí)候非常的細(xì)心,不能出現(xiàn)任何漏洞。
5.2.1 概念結(jié)構(gòu)設(shè)計(jì)
從數(shù)據(jù)需求分析中得出系統(tǒng)的實(shí)體屬性圖。
陳洪:基于SSH框架的學(xué)生檔案信息管理
級、年齡等。
如圖5-4所示:
開始用戶登錄用戶名密碼是否正確N返回登錄頁面Y進(jìn)入主頁面結(jié)束
圖5-4學(xué)生實(shí)體屬性圖
5.2.5 公告實(shí)體屬性圖
公告實(shí)體屬性圖描述的是公告信息,包括公告編號、標(biāo)題、內(nèi)容、時(shí)間等。如圖5-5所示:
開始用戶登錄用戶名密碼是否正確N返回登錄頁面Y進(jìn)入主頁面結(jié)束
圖5-5公告實(shí)體屬性圖
5.2.6成績實(shí)體屬性圖
成績屬性圖描述的是學(xué)生的成績信息,包括成績編號、學(xué)生編號、成績、考核時(shí)間等。
如圖5-6所示:
陳洪:基于SSH框架的學(xué)生檔案信息管理
開始用戶登錄用戶名密碼是否正確N返回登錄頁面Y進(jìn)入主頁面結(jié)束
圖5-7系統(tǒng)E-R圖
5.3 數(shù)據(jù)庫表設(shè)計(jì)
5.3.1 管理員信息表
管理員信息表用來存放管理員信息,其中包括管理員編號、用戶名和用戶密碼。具體信息如表5-1所示:
表5-1管理員信息表
字段名 id name pwd
類型 Int varchar varchar
長度 4 30 20
說明
管理員編號(主鍵)
用戶名 用戶密碼
5.3.2 教師信息表
教師信息表用來存放教師信息,包括教師編號、密碼、姓名、年齡、電話等。具體信息如表5-2所示:
陳洪:基于SSH框架的學(xué)生檔案信息管理
具體信息如表5-4所示:
表5-4成績信息表
字段名 id student_id course_id Grade Time Beizhu
類型 Int Int Int Int Datetime varchar
長度 4 4 4 4 8 50
說明 成績編號(主鍵)學(xué)生學(xué)號(外鍵)考核類型編號(外鍵)
成績 考核時(shí)間 備注
5.3.5 公告信息表
公告信息表用來存放公告信息,包括公告編號、標(biāo)題、內(nèi)容、時(shí)間等。具體信息如表5-5所示:
表5-5公告信息表
字段名 id Title Content Time
數(shù)據(jù)類型 Int varchar varchar Datetime
長度 4 20 100 8
功能描述 編號(主鍵)
標(biāo)題 內(nèi)容 時(shí)間
5.4 系統(tǒng)開發(fā)工具與開發(fā)模式的選擇
5.4.1 系統(tǒng)開發(fā)工具
本系統(tǒng)主要的架構(gòu)使用的是SSH框架,結(jié)合Jsp技術(shù)對前端的設(shè)計(jì)進(jìn)行的系統(tǒng)開發(fā)。Jsp技術(shù)安全性較高,可隨處運(yùn)行,并且與各個(gè)開發(fā)軟件兼容;SSH框架則為開發(fā)者提供了一個(gè)系統(tǒng)的總體框架,讓開發(fā)者的工作更加系統(tǒng)性的進(jìn)行,其中Struts2作為系統(tǒng)的基礎(chǔ)框架,也就是為系統(tǒng)搭建一個(gè)大致的架構(gòu),Hibernate則是負(fù)責(zé)連接數(shù)據(jù)庫的,通過Spring框架對這兩個(gè)框架進(jìn)行管理,讓
陳洪:基于SSH框架的學(xué)生檔案信息管理
綜上所述,本系統(tǒng)所采用的B/S架構(gòu)也是現(xiàn)在較為流行的開發(fā)架構(gòu)相對于其他架構(gòu)有著不容忽視的優(yōu)勢,這也將會是將來開發(fā)模式的主要發(fā)展趨勢。
0-
陳洪:基于SSH框架的學(xué)生檔案信息管理
圖6-2 登錄失敗
具體流程如圖6-3所示:
開始用戶登錄用戶名密碼是否正確N返回登錄頁面Y進(jìn)入主頁面結(jié)束
圖6-3 登錄程序流程圖
陳洪:基于SSH框架的學(xué)生檔案信息管理
如圖6-6所示:
圖6-6 課程管理界面
6.2.4 錄入教師信息界面
這是錄入教師信息的功能界面,如圖6-7所示:
圖6-7錄入教師界面
6.2.5錄入學(xué)生信息界面
這是錄入學(xué)生信息的功能界面,如圖6-8所示:
陳洪:基于SSH框架的學(xué)生檔案信息管理
6.3.3 學(xué)生成績管理界面
教師可以按照考試課程、考試時(shí)間和考試類型錄入學(xué)生成績信息,如圖6-11所示:
圖6-11學(xué)生成績管理界面
6.4 學(xué)生登錄模塊
6.4.1 查看個(gè)人信息界面
學(xué)生可以查看個(gè)人信息。如圖6-12所示:
圖6-12 個(gè)人信息界面
6.4.2 課程查詢界面
學(xué)生可以查看課程信息,如圖6-13所示:
陳洪:基于SSH框架的學(xué)生檔案信息管理 軟件測試與分析
系統(tǒng)測試是設(shè)計(jì)的最后一項(xiàng),也是至關(guān)重要的一項(xiàng),只有測試通過新的系統(tǒng)才算完成,所以測試在開發(fā)過程中也占有舉足輕重的地位。軟件測試有專門的測試人員負(fù)責(zé),這和開發(fā)人員的工作是完全不同。如今的軟件測試趨向于開發(fā)的每個(gè)階段都應(yīng)該包含測試,該觀點(diǎn)的依據(jù)是早發(fā)現(xiàn)問題早解決,這樣也可以防止影響到后續(xù)工作的進(jìn)行。
7.1 軟件測試的重要性
軟件測試在整個(gè)的軟件開發(fā)中占有著重要的地位,這是軟件交付于客戶之前最后的檢測手段。就相當(dāng)于汽車量產(chǎn)之前的撞墻檢查一樣,這也是從用戶的“安全問題”考慮。所謂的軟件測試就是在運(yùn)行成功的程序上尋找Bug,檢測出的錯(cuò)誤一般都是運(yùn)行時(shí)不會出現(xiàn)的,也是開發(fā)人員看不出來的,這就是所謂的Bug。而現(xiàn)在推崇的逐步測試是在開發(fā)的每個(gè)階段都進(jìn)行測試,這樣能更好的消除開發(fā)過程中所遇到的問題,更好的完成開發(fā)。
實(shí)際上,對于一個(gè)軟件的開發(fā)來說,不論具有多么熟練的開發(fā)技術(shù)、采用怎樣完善的方法,都會有錯(cuò)誤產(chǎn)生。開發(fā)人員能夠做到的只能是減少錯(cuò)誤的引入而不能杜絕錯(cuò)誤的產(chǎn)生,這些引入的錯(cuò)誤就需要測試來將其找出來,而軟件中的錯(cuò)誤的數(shù)量也是需要通過測試計(jì)算的,所以說測試在軟件開發(fā)中占有著舉足輕重的地位。只要有開發(fā)有程序就一定會有測試。而且據(jù)統(tǒng)計(jì)表明,在系統(tǒng)的開發(fā)中,并不是大家想的測試肯定比開發(fā)工作量小,這恰恰是相反的,測試的工作量一般情況下都會比開發(fā)的工作量多40%。而且在開發(fā)中,測試的成本也是占有很大部分的,一般來說都會占有30%到50%。如果把維護(hù)階段也考慮在內(nèi),討論整個(gè)軟件生存期時(shí),測試的成本比例也許會有所降低,但實(shí)際上維護(hù)工作相當(dāng)于二次開發(fā),乃至多次開發(fā),其中必定還包含有許多測試工作。
7.2 系統(tǒng)的實(shí)際測試
軟件測試主要分為黑盒測試和白盒測試。黑盒測試:也稱功能測試,數(shù)據(jù)驅(qū)動測試等,它將待測對象堪稱是一個(gè)黑盒子,在完全不考慮程序的內(nèi)部結(jié)構(gòu)和特性的情況下,只依據(jù)規(guī)格說明書檢查程序的功能是否能正常使用[15]。白盒測試:
陳洪:基于SSH框架的學(xué)生檔案信息管理 結(jié)
論
8.1 系統(tǒng)的優(yōu)勢
本系統(tǒng)是針對學(xué)生檔案信息的管理而開發(fā),為了讓管理者更為輕松的管理學(xué)生檔案信息。本系統(tǒng)能夠條理化的錄入學(xué)生信息、教師信息、課程信息和公告信息,相比傳統(tǒng)方式更加便捷;在查詢時(shí),可以根據(jù)個(gè)人需要的條件進(jìn)行查詢,如:根據(jù)學(xué)生學(xué)號查詢學(xué)生成績,根據(jù)課程信息查詢所有該課程的學(xué)生成績等;管理時(shí)管理員只需要對后臺數(shù)據(jù)庫進(jìn)行管理,面對龐大的數(shù)據(jù)也可以輕松應(yīng)對。
8.2 系統(tǒng)的不足
所謂人無完人,本系統(tǒng)也有不足之處,例如系統(tǒng)采用B/S架構(gòu)開發(fā),所以管理工作只能在瀏覽器上進(jìn)行,沒有客戶端方便;系統(tǒng)目前只支持Tomcat 6.0服務(wù)器;系統(tǒng)目前只有對數(shù)據(jù)信息的基本增刪改查功能,在以后的學(xué)習(xí)中一定對其完善。
8.3 總結(jié)
經(jīng)過這幾個(gè)月的系統(tǒng)開發(fā),讓我獲益匪淺。由于之前對SSH架構(gòu)掌握的不是很熟練,所以在開發(fā)過程中也在自學(xué),這也算是自學(xué)成功的勞動成果。在整個(gè)設(shè)計(jì)過程中,由主體SSH框架引申開展的一系列功能均能良好實(shí)現(xiàn)。此外,在開發(fā)過程中也對之前的Jsp技術(shù)和MySQL數(shù)據(jù)庫的相關(guān)知識復(fù)習(xí),在運(yùn)用起來更為熟練。在通過開發(fā)后期對系統(tǒng)的調(diào)試后,系統(tǒng)功能正常運(yùn)行,基本功能都順利實(shí)現(xiàn)。而且本系統(tǒng)界面美觀,操作方便,符合大眾要求。不過部分細(xì)節(jié)可能由于時(shí)間倉促加上系統(tǒng)開發(fā)經(jīng)驗(yàn)上的經(jīng)驗(yàn)不足,系統(tǒng)在設(shè)計(jì)過程中不可避免地遇到了一些諸如以下的一些問題,如:
(1)起初對數(shù)據(jù)庫的構(gòu)建不夠完善,導(dǎo)致開發(fā)初期的部分?jǐn)?shù)據(jù)取不到值;(2)在前端頁面的跳轉(zhuǎn)中,由于頁面較多,出現(xiàn)了一些跳轉(zhuǎn)錯(cuò)誤;(3)在測試階段,在使用了Tomcat7.0服務(wù)器出現(xiàn)了Session error的錯(cuò)誤。不過在后期的系統(tǒng)完善方面,這些問題都受到了重視,也都得到了解決,使得本系統(tǒng)真正的符合了開發(fā)者和用戶的需求,也更加規(guī)范、科學(xué)。
0-
陳洪:基于SSH框架的學(xué)生檔案信息管理
參 考 文 獻(xiàn)
[1] 劉佳.基于JSP&Servlet圖書信息管理系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)[D].天津大學(xué),2012.[2] Peluso M, Takizawa P.Student involvement in the development of integrated curricula.[J].Medical Education, 2010, 44(11):1120–1121.0.[3] 馬鐸.基于SSH技術(shù)的小區(qū)車輛管理系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[J].信息與電腦:理論版, 2013,(3).[4] 李寧.Java Web編程實(shí)戰(zhàn)寶典[M].北京:清華大學(xué)出版,2014: 41.[5] 包子建.基于B/S模式和JSP技術(shù)的教師辦公管理系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)[D].華東師范大學(xué), 2006.DOI:10.7666/d.y1015368.[6] 王飛飛.MySQL數(shù)據(jù)庫應(yīng)用從入門到精通[M].北京:中國鐵道出版社,2014: 36.[7] 李鑒.開源技術(shù)Struts、Spring、Hibernate在MIS開發(fā)中的應(yīng)用研究[D].北方民族大學(xué), 2008.[8] 曹大有, 王瑜.基于MyEclipse的Hibernate持久層框架的開發(fā)過程[J].計(jì)算機(jī)系統(tǒng)應(yīng)用, 2007,(12):101-104.DOI:10.3969/j.issn.1003-3254.2007.12.025.[9] 張曉梅.基于J2EE的教學(xué)實(shí)驗(yàn)管理系統(tǒng)的設(shè)計(jì)與開發(fā)[D].山東大學(xué), 2006.DOI:10.7666/d.y982177.[10] 年軼, 林琳.E-R圖向關(guān)系模型轉(zhuǎn)換規(guī)則的研究[J].微型電腦應(yīng)用, 2004, 20(1):12-14.DOI:10.3969/j.issn.1007-757X.2004.01.002.[11] 李寧.Java Web編程實(shí)戰(zhàn)寶典[M].北京:清華大學(xué)出版,2014: 72.[12] 王飛飛.MySQL數(shù)據(jù)庫應(yīng)用從入門到精通[M].北京:中國鐵道出版社,2014: 36.[13] CSDN博客頻道.關(guān)于B/S架構(gòu)和C/S架構(gòu)探析[J].2006.http://blog.csdn.net/yuelengxin/article/details/593561.[14] 王宇.無線局域網(wǎng)基本原理及前沿應(yīng)用[J].中國數(shù)據(jù)通信,2003,5(8):34-39.[15] 薩默維爾.軟件工程(第九版)[M].北京:機(jī)械工業(yè)出版社,2011: 78-81.2-
第四篇:色彩構(gòu)成小結(jié)
《色彩構(gòu)成》學(xué)習(xí)小結(jié)
在經(jīng)過了平面構(gòu)成的學(xué)習(xí)之后,我們又開始了色彩構(gòu)成的學(xué)習(xí)。對于色彩構(gòu)成,它不只向平面構(gòu)成那樣只是通過點(diǎn)線面以及明暗來表現(xiàn)畫面的美感,而是在此基礎(chǔ)上又增添了色彩的結(jié)構(gòu),這使得畫面更具有鮮明的美感。
對于色彩構(gòu)成,它有別于一般色彩寫生,而強(qiáng)調(diào)全面了解掌握色彩基本知識,側(cè)重于對色彩規(guī)律的科學(xué)訓(xùn)練與主觀運(yùn)用,其目的是更有效地應(yīng)到我們觀察學(xué)習(xí)自然知識,啟發(fā)創(chuàng)作靈感,執(zhí)導(dǎo)作品實(shí)踐。
在色彩學(xué)習(xí)過程中,我們涉及到了色彩學(xué)的結(jié)構(gòu)體系。即人類生活經(jīng)驗(yàn)及色彩應(yīng)用的各方面構(gòu)筑的結(jié)構(gòu)體系,色彩文化史、地域文化色彩、宗教文化色彩、民俗學(xué)習(xí)過程中色彩等。還有色彩的美學(xué)、色彩心理學(xué)、色彩與社會、色彩與自然、色彩與藝術(shù)等。色彩的構(gòu)成藝術(shù),人類創(chuàng)造的造型視覺色彩藝術(shù),自然界物質(zhì)色彩藝術(shù)規(guī)律等。同時(shí)色彩構(gòu)成:即色彩的相互作用,是從人對色彩的知覺和心理效果出發(fā),用科學(xué)的分析方法,把復(fù)雜的色彩現(xiàn)象還原為基本要素,利用色彩在空間量與質(zhì)上的變幻性,按照一定的規(guī)律去組合各構(gòu)成之間的關(guān)系,再創(chuàng)造新的色彩效果的過程。
在學(xué)習(xí)色彩構(gòu)成的過程中不僅讓我學(xué)到新的知識還讓我能夠了解到以前所模糊的知識。,我以前認(rèn)為光的三原色與色彩的三原色相同。而在色彩的基本原理學(xué)習(xí)中我卻了解到了色素的三原色分別是品紅、黃、青,對于光的三原色則是紅、綠、藍(lán)。這在以前的學(xué)習(xí)中,我是經(jīng)常把它們搞混淆的。而另一方面色彩的原色,以及混合色等。在色彩學(xué)習(xí)中,我最感興趣的是色彩對比。其中分為高調(diào),中調(diào),低調(diào)這三種。而在每一種中又分三個(gè)長調(diào)、中調(diào)、短調(diào),例如在高調(diào)中有高長調(diào)、高中調(diào)、高短調(diào)。其他的各調(diào)子也是如此。不同的調(diào)子給人以不同的感覺如高長調(diào)給人積極、明快、活潑、刺激等感覺。在對比的學(xué)習(xí)中還有同類色對比;類似色對比;鄰近色對比。同時(shí)純度的對比;冷暖對比;色度對比;形狀對比;面積對比等。值得一提的是在面積對比中在不同顏色的情況下同面積對比效果強(qiáng)烈,而在不同面積的情況下則較弱。
色彩還給人情感的變化,一、色彩的視覺形象。
二、色彩的心理形象。
三、色彩的聯(lián)想。
四、色彩的象征。有意思的是在色彩的情感中會給人帶來色彩的錯(cuò)視效果,它又分為同時(shí)對比的錯(cuò)視形象和連續(xù)對比的錯(cuò)視形象。色彩給人的心理
感知有色彩的冷暖,以及膨脹收縮感、輕重感、軟硬感等。之所以有這些感覺不是由于色彩的本身原因,而是人們的主觀感覺以至于產(chǎn)生心里的聯(lián)想。而其聯(lián)想又有具體聯(lián)想,抽象聯(lián)想和共感聯(lián)想。在色彩的象征中本人最喜歡的紅色為興奮色。
對于色彩的意義,在審美方面色彩是人類表達(dá)情感的語言手段。色彩作為情緒信息的載體傳遞與文化背景,歷史延續(xù),意識觀念,傳統(tǒng)習(xí)俗,風(fēng)土人情,生活方式有關(guān)。色彩設(shè)計(jì)的意義其要解決的是創(chuàng)造個(gè)性化的色彩綜合美,是將符合藝術(shù)規(guī)律的色彩結(jié)合應(yīng)用于我們的設(shè)計(jì)實(shí)踐中去。
第五篇:struts2代碼分析
1.Struts2架構(gòu)圖和請求處理流程
請求首先通過Filter chain,F(xiàn)ilter主要包括ActionContextCleanUp,它主要清理當(dāng)前線程的ActionContext和Dispatcher;FilterDispatcher主要通過AcionMapper來決定需要調(diào)用哪個(gè)Action。
ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里創(chuàng)建ActionProxy,ActionProxy創(chuàng)建ActionInvocation,然后ActionInvocation調(diào)用Interceptors,執(zhí)行Action本身,創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實(shí)現(xiàn)PreResultListener。
2.Struts2部分類介紹
這部分從Struts2參考文檔中翻譯就可以了。
ActionMapper
ActionMapper其實(shí)是HttpServletRequest和Action調(diào)用請求的一個(gè)映射,它屏蔽了Action對于Request等java Servlet類的依賴。Struts2中它的默認(rèn)實(shí)現(xiàn)類是DefaultActionMapper,ActionMapper很大的用處可以根據(jù)自己的需要來設(shè)計(jì)url格式,它自己也有Restful的實(shí)現(xiàn),具體可以參考文檔的docs¥actionmapper.html。
ActionProxy&ActionInvocation
Action的一個(gè)代理,由ActionProxyFactory創(chuàng)建,它本身不包括Action實(shí)例,默認(rèn)實(shí)現(xiàn)DefaultActionProxy是由ActionInvocation持有Action實(shí)例。ActionProxy作用是如何取得Action,無論是本地還是遠(yuǎn)程。而ActionInvocation的作用是如何執(zhí)行Action,攔截器的功能就是在ActionInvocation中實(shí)現(xiàn)的。
ConfigurationProvider&Configuration
ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其實(shí)現(xiàn)類XmlConfigurationProvider及其子類StrutsXmlConfigurationProvider來解析。
3.Struts2請求流程
1、客戶端發(fā)送請求
2、請求先通過ActionContextCleanUp-->FilterDispatcher
3、FilterDispatcher通過ActionMapper來決定這個(gè)Request需要調(diào)用哪個(gè)Action
4、如果ActionMapper決定調(diào)用某個(gè)Action,F(xiàn)ilterDispatcher把請求的處理交給ActionProxy,這兒已經(jīng)轉(zhuǎn)到它的Delegate--Dispatcher來執(zhí)行
5、ActionProxy根據(jù)ActionMapping和ConfigurationManager找到需要調(diào)用的Action類
6、ActionProxy創(chuàng)建一個(gè)ActionInvocation的實(shí)例
7、ActionInvocation調(diào)用真正的Action,當(dāng)然這涉及到相關(guān)攔截器的調(diào)用
8、Action執(zhí)行完畢,ActionInvocation創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實(shí)現(xiàn)PreResultListener。添加PreResultListener可以在Interceptor中實(shí)現(xiàn)。
首先強(qiáng)調(diào)一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個(gè)線程都有自己獨(dú)立的實(shí)例變量,互不相干.接下來就從Dispatcher開始看起,先看其構(gòu)造函數(shù):
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
我們再看在FilterDispatcher創(chuàng)建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對配置文件的屬性進(jìn)行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
創(chuàng)建Dispatcher之后,來看init()方法
init()方法是用來Load用戶配置文件,資源文件以及默認(rèn)的配置文件.主要分七步走,看下面注釋
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager
=
new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對象
//在這里面會循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
}
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager
=
new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對象
//在這里面會循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對配置文件的屬性進(jìn)行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
throw
}
loadSettings(props, defaultSettings);
}
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
throw
}
new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e);
loadSettings(props, defaultSettings);
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// Load settings
InputStream in = null;
try {
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props
//這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
props.setProperty(name, settings.getLocationImpl(name));
}
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings =
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
settings.getImpl(name),// Load settings
InputStream in = null;
try {
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props
//這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
props.setProperty(name, settings.getLocationImpl(name));
}
}
再來看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations(){
settings.getImpl(name), //首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
用
默
認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
String configPaths = initParams.get(“config”);
if(configPaths == null){
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);
for(String file : files){
if(file.endsWith(“.xml”)){
if(“xwork.xml”.equals(file)){
//XmlConfigurationProvider負(fù)責(zé)解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider來解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
private void init_TraditionalXmlConfigurations(){
//首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
用
默
認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
String configPaths = initParams.get(“config”);
if(configPaths == null){
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);
for(String file : files){
if(file.endsWith(“.xml”)){
if(“xwork.xml”.equals(file)){
//XmlConfigurationProvider負(fù)責(zé)解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider來解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
對于其它配置文件只用接口。
類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map
addAction()方法負(fù)責(zé)讀取
loadInterceptorStack()方法負(fù)責(zé)將
StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實(shí)現(xiàn)ConfigurationProviderloadInterceptorStacks()方法負(fù)責(zé)將
而上面的方法最終會被addPackage()方法調(diào)用,addPackage又會被Provider的loadPackages()調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對象中。
protected PackageConfig
addPackage(Element
packageElement)
throws ConfigurationException {
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
if(newPackage.isNeedsRefresh()){
return newPackage.build();
}
// add result types(and default result)to this package
addResultTypes(newPackage, packageElement);
// load the interceptors and interceptor stacks for this package
loadInterceptors(newPackage, packageElement);
// load the default interceptor reference for this package
loadDefaultInterceptorRef(newPackage, packageElement);
// load the default class ref for this package
loadDefaultClassRef(newPackage, packageElement);
// load the global result list for this package
loadGlobalResults(newPackage, packageElement);
// load the global exception handler list for this package
loadGobalExceptionMappings(newPackage, packageElement);
// get actions
NodeList actionList = packageElement.getElementsByTagName(“action”);
for(int i = 0;i < actionList.getLength();i++){
Element actionElement =(Element)actionList.item(i);
addAction(actionElement, newPackage);
}
// load the default action reference for this package
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}
loadConfigurationFiles解析讀取xml中的內(nèi)容
private List
loadConfigurationFiles(String
fileName,Element includeElement){
...//通過DomHelper調(diào)用SAX進(jìn)行解析xml
doc = DomHelper.parse(in, dtdMappings);
...Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for(int i = 0;i < childSize;i++){
Node childNode = children.item(i);
if(childNode instanceof Element){
Element child =(Element)childNode;
final String nodeName = child.getNodeName();
if(“include”.equals(nodeName)){
String includeFileName = child.getAttribute(“file”);
//解析每個(gè)action配置是,對于include文件可以使用通配符*來進(jìn)行配置
//如Struts.xml中可配置成
if(includeFileName.indexOf('*')!=-1){
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Vector
for(String match : wildcardMatches){
//遞歸Load子file中的
docs.addAll(loadConfigurationFiles(match, child));
}
} else {
docs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
docs.add(doc);
loadedFileUrls.add(url.toString());
...return docs;
}
首先強(qiáng)調(diào)一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個(gè)線程都有自己獨(dú)立的實(shí)例變量,互不相干.接下來就從Dispatcher開始看起,先看其構(gòu)造函數(shù):
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
我們再看在FilterDispatcher創(chuàng)建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對配置文件的屬性進(jìn)行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
創(chuàng)建Dispatcher之后,來看init()方法
init()方法是用來Load用戶配置文件,資源文件以及默認(rèn)的配置文件.主要分七步走,看下面注釋
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對象
//在這里面會循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
new
}
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對象
//在這里面會循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是DefaultConfiguration的#reload()方法
Container container = init_PreloadConfiguration();
container.inject(this);
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
if(!dispatcherListeners.isEmpty()){
for(DispatcherListener l : dispatcherListeners){
l.dispatcherInitialized(this);
}
}
new
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對配置文件的屬性進(jìn)行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
throw
}
loadSettings(props, defaultSettings);
}
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
throw
}
loadSettings(props, defaultSettings);
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// Load settings
InputStream in = null;
try {
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props
//這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
props.setProperty(name, settings.getLocationImpl(name));
}
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
settings.getImpl(name),if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// Load settings
InputStream in = null;
try {
in = settingsUrl.openStream();
settings.load(in);
} catch(IOException e){
throw new StrutsException(“Could not load ” + name + “.properties:” + e, e);
} finally {
if(in!= null){
try {
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props
//這個(gè)props是register的一個(gè)入?yún)?protected void loadSettings(LocatableProperties props, final Settings settings){
// We are calling the impl methods to get around the single instance of Settings that is expected
for(Iterator i = settings.listImpl();i.hasNext();){
String name =(String)i.next();
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
props.setProperty(name, settings.getLocationImpl(name));
}
}
再來看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations(){
//首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
settings.getImpl(name),用默認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
String configPaths = initParams.get(“config”);
if(configPaths == null){
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);
for(String file : files){
if(file.endsWith(“.xml”)){
if(“xwork.xml”.equals(file)){
//XmlConfigurationProvider負(fù)責(zé)解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider來解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
private void init_TraditionalXmlConfigurations(){
//首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
用
默
認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
String configPaths = initParams.get(“config”);
if(configPaths == null){
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);
for(String file : files){
if(file.endsWith(“.xml”)){
if(“xwork.xml”.equals(file)){
//XmlConfigurationProvider負(fù)責(zé)解析xwork.xml
configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
} else {
//其它xml都是由StrutsXmlConfigurationProvider來解析
configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException(“Invalid configuration file name”);
}
}
}
對于其它配置文件只用接口。
類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map
addAction()方法負(fù)責(zé)讀取
loadInterceptorStack()方法負(fù)責(zé)將
loadInterceptorStacks()方法負(fù)責(zé)將
而上面的方法最終會被addPackage()方法調(diào)用,addPackage又會被Provider的loadPackages()調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對象中。
protected PackageConfig
addPackage(Element
packageElement)
throws ConfigurationException {
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
if(newPackage.isNeedsRefresh()){
return newPackage.build();
}
// add result types(and default result)to this package
addResultTypes(newPackage, packageElement);
// load the interceptors and interceptor stacks for this package
loadInterceptors(newPackage, packageElement);
// load the default interceptor reference for this package
loadDefaultInterceptorRef(newPackage, packageElement);
// load the default class ref for this package
loadDefaultClassRef(newPackage, packageElement);
// load the global result list for this package
loadGlobalResults(newPackage, packageElement);
// load the global exception handler list for this package
loadGobalExceptionMappings(newPackage, packageElement);
// get actions
NodeList actionList = packageElement.getElementsByTagName(“action”);
for(int i = 0;i < actionList.getLength();i++){
Element actionElement =(Element)actionList.item(i);
addAction(actionElement, newPackage);
}
// load the default action reference for this package
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}
loadConfigurationFiles解析讀取xml中的內(nèi)容
private List
loadConfigurationFiles(String
fileName, includeElement){
...//通過DomHelper調(diào)用SAX進(jìn)行解析xml
doc = DomHelper.parse(in, dtdMappings);
...Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for(int i = 0;i < childSize;i++){
Node childNode = children.item(i);
if(childNode instanceof Element){
Element child =(Element)childNode;
final String nodeName = child.getNodeName();
if(“include”.equals(nodeName)){
String includeFileName = child.getAttribute(“file”);
//解析每個(gè)action配置是,對于include文件可以使用通配符*來進(jìn)行配置
//如Struts.xml中可配置成
if(includeFileName.indexOf('*')!=-1){
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Element
Vector
for(String match : wildcardMatches){
//遞歸Load子file中的
docs.addAll(loadConfigurationFiles(match, child));
}
} else {
docs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
docs.add(doc);
loadedFileUrls.add(url.toString());
...return docs;
}
接下來第三步:init_LegacyStrutsProperties()調(diào)用的是調(diào)用的是LegacyPropertiesConfigurationProvider 通過比較前
面
DefaultPropertiesProvider
與
調(diào)
用的是LegacyPropertiesConfigurationProvider.發(fā)現(xiàn)DefaultPropertiesProvider繼承自后者,但重寫了register()方法,主要是生成PropertiesSetting的不同,前者是根據(jù)org/apache/struts2/default.properties 后者是根據(jù)struts.properties 我們展開register()中的Settings.getInstance(),最后是調(diào)用getDefaultInstance()
private static Settings getDefaultInstance(){
if(defaultImpl == null){
// Create bootstrap implementation
//不帶參數(shù)的DefaultSettings(),區(qū)別與DefaultPropertiesProvider中直接帶default.properties參數(shù)
//不帶參數(shù)就是默認(rèn)為struts.propertes,并且加載struts.custom.properties所定義的properties文件
defaultImpl = new DefaultSettings();
// Create default implementation
try {
//STRUTS_CONFIGURATION為:struts.configuration
//在struts.proterties中查找struts.configuration的值,這個(gè)值必須是org.apache.struts2.config.Configuration接口的實(shí)現(xiàn)類
//所以我有個(gè)困惑就是在下面的轉(zhuǎn)換當(dāng)中怎么將Configuration轉(zhuǎn)換成Setting類型的...//這一點(diǎn)先放下了,有時(shí)間再研究
String className = get(StrutsConstants.STRUTS_CONFIGURATION);
if(!className.equals(defaultImpl.getClass().getName())){
try {
// singleton instances shouldn't be built accessing request or session-specific context data
defaultImpl oader().loadClass(className), null);
} catch(Exception e){
LOG.error(“Settings:
}
}
} catch(IllegalArgumentException ex){
// ignore
}
private static Settings getDefaultInstance(){
if(defaultImpl == null){
// Create bootstrap implementation
//不帶參數(shù)的DefaultSettings(),區(qū)別與DefaultPropertiesProvider中直接帶default.properties參數(shù)
//不帶參數(shù)就是默認(rèn)為struts.propertes,并且加載struts.custom.properties所定義的properties文件
defaultImpl = new DefaultSettings();
// Create default implementation
try {
//STRUTS_CONFIGURATION為:struts.configuration
//在struts.proterties中查找struts.configuration的值,這個(gè)值必須是
Could
not
instantiate
the struts.configuration object, substituting the default implementation.”, e);
=
(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的實(shí)現(xiàn)類
//所以我有個(gè)困惑就是在下面的轉(zhuǎn)換當(dāng)中怎么將Configuration轉(zhuǎn)換成Setting類型的...//這一點(diǎn)先放下了,有時(shí)間再研究
String className = get(StrutsConstants.STRUTS_CONFIGURATION);
if(!className.equals(defaultImpl.getClass().getName())){
try {
// singleton instances shouldn't be built accessing request or session-specific context data
defaultImpl oader().loadClass(className), null);
} catch(Exception e){
LOG.error(“Settings:
}
}
} catch(IllegalArgumentException ex){
// ignore
}
在2.1.6中去掉了第四步:init_ZeroConfiguration();第五步是自定義的configProviders
private void init_CustomConfigurationProviders(){
//從這里可以看到可以將自定義的Provider定義在web.xml中FilterDispatcher的param中:configProviders
String configProvs = initParams.get(”configProviders“);
if(configProvs!= null){
String[] classes = configProvs.split(”¥¥s*[,]¥¥s*“);
for(String cname : classes){
try {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider(ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
prov
=
Could
not
instantiate
the struts.configuration object, substituting the default implementation.”, e);
=
(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassL
}
...}
}
}
private void init_CustomConfigurationProviders(){
//從這里可以看到可以將自定義的Provider定義在web.xml中FilterDispatcher的param中:configProviders
String configProvs = initParams.get(“configProviders”);
if(configProvs!= null){
String[] classes = configProvs.split(“¥¥s*[,]¥¥s*”);
for(String cname : classes){
try {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider(ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
}
...}
}
}
第六步:init_FilterInitParameters
//從這里可以看出struts.properties中的屬性不僅可以在struts.xml中以constant形式定義,而且可以在FilterDispatcher的param中定義
private void init_FilterInitParameters(){
configurationManager.addConfigurationProvider(new ConfigurationProvider(){
public void destroy(){}
public
void
init(Configuration
configuration)
throws ConfigurationException {}
public void loadPackages()throws ConfigurationException {}
public boolean needsReload(){ return false;}
prov
=
public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException {
props.putAll(initParams);//在這里實(shí)現(xiàn)滴~
}
});
}
//從這里可以看出struts.properties中的屬性不僅可以在struts.xml中以constant形式定義,而且可以在FilterDispatcher的param中定義
private void init_FilterInitParameters(){
configurationManager.addConfigurationProvider(new ConfigurationProvider(){
public void destroy(){}
public
void
init(Configuration
configuration)
throws ConfigurationException {}
public void loadPackages()throws ConfigurationException {}
public boolean needsReload(){ return false;}
public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException {
props.putAll(initParams);//在這里實(shí)現(xiàn)滴~
}
});
}
第七步:init_AliasStandardObjects,使用BeanSelectionProvider 這是將配置文件中定義的
接下來是看怎樣調(diào)用這些ConfigurationProviders 展開init_PreloadConfiguration()
private Container init_PreloadConfiguration(){
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
return container;
}
//再看getConfiguration()
public synchronized Configuration getConfiguration(){
if(configuration == null){
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//重點(diǎn)就是這個(gè)reloadContainer
configuration.reloadContainer(getContainerProviders());
} catch(ConfigurationException e){
setConfiguration(null);
throw new ConfigurationException(“Unable to load configuration.”, e);
}
} else {
conditionalReload();
}
return configuration;
}
private Container init_PreloadConfiguration(){
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
boolean reloadi18n
=
Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
return container;
}
//再看getConfiguration()
public synchronized Configuration getConfiguration(){
if(configuration == null){
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//重點(diǎn)就是這個(gè)reloadContainer
configuration.reloadContainer(getContainerProviders());
} catch(ConfigurationException e){
setConfiguration(null);
throw new ConfigurationException(“Unable to load configuration.”, e);
}
} else {
conditionalReload();
}
return configuration;
}
展開DefaultConfiguration中的reloadContainer
public synchronized List
reloadContainer(List
packageContexts.clear();
loadedFileNames.clear();
List
packageProviders = new ArrayList
();
//Struts2(xwork2)用Container來完成依賴注入的功能
//首先初始化一個(gè)ContainerBuilder,再由builder來保存接口與實(shí)現(xiàn)類或工廠類的對應(yīng)關(guān)系
//然后通過builder.create(boolean)方法產(chǎn)生container
//由container.getInstance(Class);就可以得到接口的實(shí)現(xiàn)實(shí)例了
//這一部分比較復(fù)雜,后面研究完成了,會單獨(dú)拿出來講,這里先弄清楚Xwork依賴注入的實(shí)現(xiàn)步驟就可以了
ContainerProperties props = new ContainerProperties();
ContainerBuilder builder = new ContainerBuilder();
for(final ContainerProvider containerProvider : providers)
{
//循環(huán)調(diào)用ConfigurationProvider的init和register方法,明白了吧,在這里統(tǒng)一循環(huán)調(diào)用
containerProvider.init(this);
containerProvider.register(builder, props);
}
props.setConstants(builder);
//注入依賴關(guān)系,在這里并不產(chǎn)生實(shí)例
builder.factory(Configuration.class, new Factory
public Configuration create(Context context)throws Exception {
return DefaultConfiguration.this;
}
});
ActionContext oldContext = ActionContext.getContext();
try {
// Set the bootstrap container for the purposes of factory creation
Container bootstrap = createBootstrapContainer();
setContext(bootstrap);
//create已經(jīng)注入依賴關(guān)系的Container
container = builder.create(false);
setContext(container);
objectFactory = container.getInstance(ObjectFactory.class);
// Process the configuration providers first
for(final ContainerProvider containerProvider : providers)
{
if(containerProvider instanceof PackageProvider){
container.inject(containerProvider);
//調(diào)用PackageProvider的loadPackages()方法,這里主要是針對XmlConfigurationProvider和StrutsXmlConfigurationProvider
((PackageProvider)containerProvider).loadPackages();
packageProviders.add((PackageProvider)containerProvider);
}
}
// Then process any package providers from the plugins
Set
packageProviderNames
= container.getInstanceNames(PackageProvider.class);
if(packageProviderNames!= null){
for(String name : packageProviderNames){
PackageProvider
provider.init(this);
provider.loadPackages();
packageProviders.add(provider);
}
}
rebuildRuntimeConfiguration();
} finally {
if(oldContext == null){
ActionContext.setContext(null);
}
}
return packageProviders;
}
Dispatcher已經(jīng)在之前講過,這就好辦了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。
public void init(FilterConfig filterConfig)throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
//創(chuàng)建dispatcher,前面都已經(jīng)講過啰
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
//注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader
dispatcher.getContainer().inject(this);
//StaticContentLoader在BeanSelectionProvider中已經(jīng)被注入了依賴關(guān)系:DefaultStaticContentLoader
//可以在struts-default.xml中的
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
provider
= container.getInstance(PackageProvider.class, name);
ActionContext.setContext(null);
}
}
public void init(FilterConfig filterConfig)throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
//創(chuàng)建dispatcher,前面都已經(jīng)講過啰
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
//注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader
dispatcher.getContainer().inject(this);
//StaticContentLoader在BeanSelectionProvider中已經(jīng)被注入了依賴關(guān)系:DefaultStaticContentLoader
//可以在struts-default.xml中的
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
ActionContext.setContext(null);
}
}
//下面來看DefaultStaticContentLoader的setHostConfig
public void setHostConfig(HostConfig filterConfig){
//讀取初始參數(shù)
pakages,調(diào)用
parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組
String param = filterConfig.getInitParameter(“packages”);
//“org.apache.struts2.static org.apache.struts2.interceptor.debugging static”
String packages = getAdditionalPackages();
if(param!= null){
packages = param + “ ” + packages;
}
this.pathPrefixes = parse(packages);
initLogging(filterConfig);
}
template //下面來看DefaultStaticContentLoader的setHostConfig
public void setHostConfig(HostConfig filterConfig){
//讀取初始參數(shù)
pakages,調(diào)用
parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組
String param = filterConfig.getInitParameter(“packages”);
//“org.apache.struts2.static org.apache.struts2.interceptor.debugging static”
String packages = getAdditionalPackages();
if(param!= null){
packages = param + “ ” + packages;
}
this.pathPrefixes = parse(packages);
initLogging(filterConfig);
}
現(xiàn)在回去doFilter的方法,每當(dāng)有一個(gè)Request,都會調(diào)用這些Filters的doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = “FilterDispatcher_doFilter: ”;
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
//先看看ValueStackFactory所注入的實(shí)現(xiàn)類OgnlValueStackFactory
//new OgnlValueStack
ValueStack
stack
= dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
template
UtilTimerStack.push(timerKey);
//如果是multipart/form-data就用MultiPartRequestWrapper進(jìn)行包裝
//MultiPartRequestWrapper
是
StrutsRequestWrapper的子類,兩者都是HttpServletRequest實(shí)現(xiàn)
//此時(shí)在MultiPartRequestWrapper中就會把Files給解析出來,用于文件上傳
//所有request都會StrutsRequestWrapper進(jìn)行包裝,StrutsRequestWrapper是可以訪問ValueStack
//下面是參見Dispatcher的wrapRequest
// String content_type = request.getContentType();
//if(content_type!= null&&content_type.indexOf(“multipart/form-data”)!=-1){
//MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);
//request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));
//} else {
//
request = new StrutsRequestWrapper(request);
// }
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
//根據(jù)url取得對應(yīng)的Action的配置信息
//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中
mapping
=
actionMapper.getMapping(request, dispatcher.getConfigurationManager());
} catch(Exception ex){
log.error(“error getting ActionMapping”, ex);
dispatcher.sendError(request,return;
}
//如果找不到對應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等
//這兒有個(gè)例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再沒有則跳轉(zhuǎn)到
response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
=new 404
if(mapping == null){
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if(“".equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
if(staticResourceLoader.canHandle(resourcePath)){
// 在DefaultStaticContentLoader
中
:return
serveStatic
&&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“));
staticResourceLoader.findStaticResource(resourcePath, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
//正式開始Action的方法
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
request,HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = ”FilterDispatcher_doFilter: “;
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
//先看看ValueStackFactory所注入的實(shí)現(xiàn)類OgnlValueStackFactory
//new OgnlValueStack
ValueStack
stack
= dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
UtilTimerStack.push(timerKey);
//如果是multipart/form-data就用MultiPartRequestWrapper進(jìn)行包裝
//MultiPartRequestWrapperHttpServletRequest實(shí)現(xiàn)
//此時(shí)在MultiPartRequestWrapper中就會把Files給解析出來,用于文件上傳
//所有request都會StrutsRequestWrapper進(jìn)行包裝,StrutsRequestWrapper是可以訪問ValueStack
//下面是參見Dispatcher的wrapRequest
// String content_type = request.getContentType();
//if(content_type!= null&&content_type.indexOf(”multipart/form-data“)!=-1){
//MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);
//request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));
//} else {
//
request = new StrutsRequestWrapper(request);
// }
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
=new
是
StrutsRequestWrapper的子類,兩者都是
//根據(jù)url取得對應(yīng)的Action的配置信息
//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中
mapping
} catch(Exception ex){
log.error(”error getting ActionMapping“, ex);
dispatcher.sendError(request,return;
}
//如果找不到對應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等
//這兒有個(gè)例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再沒有則跳轉(zhuǎn)到404
if(mapping == null){
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if(”“.equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
if(staticResourceLoader.canHandle(resourcePath)){
// 在DefaultStaticContentLoader
中
:return
serveStatic
&&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“));
staticResourceLoader.findStaticResource(resourcePath, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
request,response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
=
actionMapper.getMapping(request, dispatcher.getConfigurationManager());
//正式開始Action的方法
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
//下面是ActionMapper接口的實(shí)現(xiàn)類 DefaultActionMapper的getMapping()方法的源代碼:
public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do
int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時(shí)找不到而且的bug
uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;
uri = dropExtension(uri, mapping);//刪除擴(kuò)展名,默認(rèn)擴(kuò)展名為action
if(uri == null){
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace
handleSpecialParameters(request, mapping);//去掉重復(fù)參數(shù)
//如果Action的name沒有解析出來,直接返回
if(mapping.getName()== null){
returnnull;
}
//下面處理形如testAction!method格式的請求路徑
if(allowDynamicMethodCalls){
// handle ”name!method“ convention.String name = mapping.getName();
int exclamation = name.lastIndexOf(”!“);//!是Action名稱和方法名的分隔符
if(exclamation!=-1){
mapping.setName(name.substring(0, exclamation));//提取左邊為name
mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method
}
}
return mapping;
}
//下面是ActionMapper接口的實(shí)現(xiàn)類 DefaultActionMapper的getMapping()方法的源代碼:
public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do
int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時(shí)找不到而且的bug
uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;
uri = dropExtension(uri, mapping);//刪除擴(kuò)展名,默認(rèn)擴(kuò)展名為action
if(uri == null){
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace
handleSpecialParameters(request, mapping);//去掉重復(fù)參數(shù)
//如果Action的name沒有解析出來,直接返回
if(mapping.getName()== null){
returnnull;
}
//下面處理形如testAction!method格式的請求路徑
if(allowDynamicMethodCalls){
// handle ”name!method“ convention.String name = mapping.getName();
int exclamation = name.lastIndexOf(”!“);//!是Action名稱和方法名的分隔符
if(exclamation!=-1){
mapping.setName(name.substring(0, exclamation));//提取左邊為name
mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method
}
}
return mapping;
}
從代碼中看出,getMapping()方法返回ActionMapping類型的對象,該對象包含三個(gè)參數(shù):Action的name、namespace和要調(diào)用的方法method。
如果getMapping()方法返回ActionMapping對象為null,則FilterDispatcher認(rèn)為用戶請求不是Action,自然另當(dāng)別論,F(xiàn)ilterDispatcher會做一件非常有意思的事:如果請求以/struts開頭,會自動查找在web.xml文件中配置的 packages初始化參數(shù),就像下面這樣:
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
FilterDispatcher會將com.lizanhong.action包下的文件當(dāng)作靜態(tài)資源處理,即直接在頁面上顯示文件內(nèi)容,不過會忽略擴(kuò)展名為class的文件。比如在com.lizanhong.action包下有一個(gè)aaa.txt的文本文件,其內(nèi)容為“中華人民共和國”,訪問
http://localhost:8081/Struts2Demo/struts/aaa.txt時(shí)會輸出txt中的內(nèi)容
FilterDispatcher.findStaticResource()方法
protectedvoid findStaticResource(String
name,HttpServletRequest
request, HttpServletResponse response)throws IOException {
if(!name.endsWith(”.class“)){//忽略class文件
//遍歷packages參數(shù)
for(String pathPrefix : pathPrefixes){
InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流
if(is!= null){
...// set the content-type header
String contentType = getContentType(name);//讀取內(nèi)容類型
if(contentType!= null){
response.setContentType(contentType);//重新設(shè)置內(nèi)容類型
}
...try {
//將讀取到的文件流以每次復(fù)制4096個(gè)字節(jié)的方式循環(huán)輸出
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
}
protectedvoid findStaticResource(String
name,HttpServletRequest
request, HttpServletResponse response)throws IOException {
if(!name.endsWith(”.class“)){//忽略class文件
//遍歷packages參數(shù)
for(String pathPrefix : pathPrefixes){
InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流
if(is!= null){
...// set the content-type header
String contentType = getContentType(name);//讀取內(nèi)容類型
if(contentType!= null){
response.setContentType(contentType);//重新設(shè)置內(nèi)容類型
}
...try {
//將讀取到的文件流以每次復(fù)制4096個(gè)字節(jié)的方式循環(huán)輸出
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
}
如果用戶請求的資源不是以/struts開頭——可能是.jsp文件,也可能是.html文件,則通過過濾器鏈繼續(xù)往下傳送,直到到達(dá)請求的資源為止。
如果getMapping()方法返回有效的ActionMapping對象,則被認(rèn)為正在請求某個(gè)Action,將調(diào)用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關(guān)鍵所在。
下面就來看serviceAction,這又回到全局變量dispatcher中了
//Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException {
//createContextMap方法主要把Application、Session、Request的key value值拷貝到Map中
Map
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack
stack
=
(ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if(nullStack){
ActionContext ctx = ActionContext.getContext();
if(ctx!= null){
stack = ctx.getValueStack();
}
}
if(stack!= null){
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
}
String timerKey = ”Handling request from Dispatcher“;
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
//創(chuàng)建一個(gè)Action的代理對象,ActionProxyFactory是創(chuàng)建ActionProxy的工廠
//參考實(shí)現(xiàn)類:DefaultActionProxy和DefaultActionProxyFactory
ActionProxy
proxy
= config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
//如果是Result,則直接轉(zhuǎn)向,關(guān)于Result,ActionProxy,ActionInvocation下一講中再分析
if(mapping.getResult()!= null){
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
//執(zhí)行Action
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if(!nullStack){
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch(ConfigurationException e){
// WW-2874 Only log error if in devMode
if(devMode){
LOG.error(”Could not find action or result“, e);
}
else {
LOG.warn(”Could not find action or result“, e);
}
sendError(request, HttpServletResponse.SC_NOT_FOUND, e);
} catch(Exception e){
sendError(request,} finally {
UtilTimerStack.pop(timerKey);
}
} 下面開始講一下主菜ActionProxy了.在這之前最好先去了解一下動態(tài)Proxy的基本知識.ActionProxy是Action的一個(gè)代理類,也就是說Action的調(diào)用是通過ActionProxy實(shí)現(xiàn)的,其實(shí)就是調(diào)用了ActionProxy.execute()方法,而該方法又調(diào)用了ActionInvocation.invoke()方法。歸根到底,最后調(diào)用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。
最后
通
過
調(diào)
用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()這里的步驟是先由ActionProxyFactory創(chuàng)建ActionInvocation和ActionProxy.public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
response,context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
response,context,ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
下面先看DefaultActionInvocation的init方法
public void init(ActionProxy proxy){
this.proxy = proxy;
Map
// Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext();
if(actionContext!= null){
actionContext.setActionInvocation(this);
}
//創(chuàng)建Action,struts2中每一個(gè)Request都會創(chuàng)建一個(gè)新的Action
createAction(contextMap);
if(pushAction){
stack.push(action);
contextMap.put(”action“, action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
// get a new List so we don't get problems with the iterator if someone changes the list
List
interceptorList
=
new ArrayList
interceptors = interceptorList.iterator();
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
}
protected void createAction(Map
// load action
String timerKey = ”actionCreate: “ + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
//默認(rèn)為SpringObjectFactory:struts.objectFactory=spring.這里非常巧妙,在struts.properties中可以重寫這個(gè)屬性
//在前面BeanSelectionProvider中通過配置文件為ObjectFactory設(shè)置實(shí)現(xiàn)類
//這里以Spring為例,這里會調(diào)到SpringObjectFactory的buildBean方法,可以通過ApplicationContext的getBean()方法得到Spring的Bean
action
=
objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
} catch(InstantiationException e){
throw new
XWorkException(”Unable
to
intantiate
Action!“,e, proxy.getConfig());
} catch(IllegalAccessException e){
throw new XWorkException(”Illegal access to constructor, is it public?", e, proxy.getConfig());
} catch(Exception e){
...} finally {
UtilTimerStack.pop(timerKey);
}
if(actionEventListener!= null){
action = actionEventListener.prepare(action, stack);
}
}
//SpringObjectFactory
public Object buildBean(String beanName, Map
Object o = null;
try {
//SpringObjectFactory
會
通
過
web.xml
中的