Denis's Java Library

          The only documentation is the code itself

          2006年2月5日 #

          Spring中的DAO和Service

               摘要: 我們開(kāi)發(fā)程序的目的是為了完成業(yè)務(wù)功能, 理想的情況下程序中的每一條語(yǔ)句都應(yīng)該是與業(yè)務(wù)直接相關(guān)的, 例如程序中不應(yīng)該出現(xiàn)連接數(shù)據(jù)庫(kù), 讀取某個(gè)字段等純技術(shù)性的操作, 而應(yīng)該是得到用戶(hù)A的基本信息等具有業(yè)務(wù)含義的操作. dao(data access object)層存在的意。。。。。  閱讀全文

          posted @ 2006-02-05 17:17 DenisLing 閱讀(1629) | 評(píng)論 (0)編輯 收藏

          2005年12月20日 #

          數(shù)據(jù)庫(kù)設(shè)計(jì)的范式大綱及一個(gè)小的實(shí)例說(shuō)明

           

          數(shù)據(jù)庫(kù)設(shè)計(jì)的范式大綱

          第一范式:

          對(duì)于表中的每一行,必須且僅僅有唯一的行值.在一行中的每一列僅有唯一的值并且具有原子性.

          第二范式:

          第二范式要求非主鍵列是主鍵的子集,非主鍵列活動(dòng)必須完全依賴(lài)整個(gè)主鍵。主鍵必須有唯一性的元素,一個(gè)主鍵可以由一個(gè)或更多的組成唯一值的列組成。一旦創(chuàng)建,主鍵無(wú)法改變,外鍵關(guān)聯(lián)一個(gè)表的主鍵。主外鍵關(guān)聯(lián)意味著一對(duì)多的關(guān)系.

          第三范式:

          第三范式要求非主鍵列互不依賴(lài).

          第四范式:

          第四范式禁止主鍵列和非主鍵列一對(duì)多關(guān)系不受約束

          第五范式:

          第五范式將表分割成盡可能小的塊,為了排除在表中所有的冗余.

           

          下面先討論前3個(gè)范式:

           

          引言

            數(shù)據(jù)庫(kù)的設(shè)計(jì)范式是數(shù)據(jù)庫(kù)設(shè)計(jì)所需要滿(mǎn)足的規(guī)范,滿(mǎn)足這些規(guī)范的數(shù)據(jù)庫(kù)是簡(jiǎn)潔的、結(jié)構(gòu)明晰的,同時(shí),不會(huì)發(fā)生插入 (insert)、刪除(delete)和更新(update)操作異常。反之則是亂七八糟,不僅給數(shù)據(jù)庫(kù)的編程人員制造麻煩,而且面目可憎,可能存儲(chǔ)了 大量不需要的冗余信息。

            設(shè)計(jì)范式是不是很難懂呢?非也,大學(xué)教材上給我們一堆數(shù)學(xué)公式我們當(dāng)然看不懂,也記不住。所以我們很多人就根本不按照范式來(lái)設(shè)計(jì)數(shù)據(jù)庫(kù)。

            實(shí)質(zhì)上,設(shè)計(jì)范式用很形象、很簡(jiǎn)潔的話(huà)語(yǔ)就能說(shuō)清楚,道明白。本文將對(duì)范式進(jìn)行通俗地說(shuō)明,并以筆者曾經(jīng)設(shè)計(jì)的一個(gè)簡(jiǎn)單論壇的數(shù)據(jù)庫(kù)為例來(lái)講解怎樣將這些范式應(yīng)用于實(shí)際工程。

            范式說(shuō)明

            第一范式(1NF):數(shù)據(jù)庫(kù)表中的字段都是單一屬性的,不可再分。這個(gè)單一屬性由基本類(lèi)型構(gòu)成,包括整型、實(shí)數(shù)、字符型、邏輯型、日期型等。

            例如,如下的數(shù)據(jù)庫(kù)表是符合第一范式的:

          字段1

          字段2

          字段3

          字段4

           

           

           

           


            而這樣的數(shù)據(jù)庫(kù)表是不符合第一范式的:

          字段1

          字段2

          字段3

          字段4

           

           

          字段3.1

          字段3.2

           







            很顯然,在當(dāng)前的任何關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)中,傻瓜也不可能做出不符合第一范式的數(shù)據(jù)庫(kù),因?yàn)檫@些DBMS不允許你把數(shù)據(jù)庫(kù)表的一列再分成二列或多列。因此,你想在現(xiàn)有的DBMS中設(shè)計(jì)出不符合第一范式的數(shù)據(jù)庫(kù)都是不可能的。

            第二范式(2NF):數(shù)據(jù)庫(kù)表中不存在非關(guān)鍵字段對(duì)任一候選關(guān)鍵字段的部分函數(shù)依賴(lài)(部分函數(shù)依賴(lài)指的是存在組合關(guān)鍵字中的某些字段決定非關(guān)鍵字段的情況),也即所有非關(guān)鍵字段都完全依賴(lài)于任意一組候選關(guān)鍵字。

            假定選課關(guān)系表為SelectCourse(學(xué)號(hào), 姓名, 年齡, 課程名稱(chēng), 成績(jī), 學(xué)分),關(guān)鍵字為組合關(guān)鍵字(學(xué)號(hào), 課程名稱(chēng)),因?yàn)榇嬖谌缦聸Q定關(guān)系:

            (學(xué)號(hào), 課程名稱(chēng)) → (姓名, 年齡, 成績(jī), 學(xué)分)

            這個(gè)數(shù)據(jù)庫(kù)表不滿(mǎn)足第二范式,因?yàn)榇嬖谌缦聸Q定關(guān)系:

            (課程名稱(chēng)) → (學(xué)分)

            (學(xué)號(hào)) → (姓名, 年齡)

            即存在組合關(guān)鍵字中的字段決定非關(guān)鍵字的情況。

            由于不符合2NF,這個(gè)選課關(guān)系表會(huì)存在如下問(wèn)題:

            (1) 數(shù)據(jù)冗余:

            同一門(mén)課程由n個(gè)學(xué)生選修,"學(xué)分"就重復(fù)n-1次;同一個(gè)學(xué)生選修了m門(mén)課程,姓名和年齡就重復(fù)了m-1次。

            (2) 更新異常:

            若調(diào)整了某門(mén)課程的學(xué)分,數(shù)據(jù)表中所有行的"學(xué)分"值都要更新,否則會(huì)出現(xiàn)同一門(mén)課程學(xué)分不同的情況。

            (3) 插入異常:

            假設(shè)要開(kāi)設(shè)一門(mén)新的課程,暫時(shí)還沒(méi)有人選修。這樣,由于還沒(méi)有"學(xué)號(hào)"關(guān)鍵字,課程名稱(chēng)和學(xué)分也無(wú)法記錄入數(shù)據(jù)庫(kù)。

            (4) 刪除異常:

            假設(shè)一批學(xué)生已經(jīng)完成課程的選修,這些選修記錄就應(yīng)該從數(shù)據(jù)庫(kù)表中刪除。但是,與此同時(shí),課程名稱(chēng)和學(xué)分信息也被刪除了。很顯然,這也會(huì)導(dǎo)致插入異常。

            把選課關(guān)系表SelectCourse改為如下三個(gè)表:

            學(xué)生:Student(學(xué)號(hào), 姓名, 年齡)

            課程:Course(課程名稱(chēng), 學(xué)分)

            選課關(guān)系:SelectCourse(學(xué)號(hào), 課程名稱(chēng), 成績(jī))

            這樣的數(shù)據(jù)庫(kù)表是符合第二范式的,消除了數(shù)據(jù)冗余、更新異常、插入異常和刪除異常。

            另外,所有單關(guān)鍵字的數(shù)據(jù)庫(kù)表都符合第二范式,因?yàn)椴豢赡艽嬖诮M合關(guān)鍵字。

             第三范式(3NF):在第二范式的基礎(chǔ)上,數(shù)據(jù)表中如果不存在非關(guān)鍵字段對(duì)任一候選關(guān)鍵字段的傳遞函數(shù)依賴(lài)則符合第三范式。所謂傳遞函數(shù)依賴(lài),指的是如 果存在"A → B → C"的決定關(guān)系,則C傳遞函數(shù)依賴(lài)于A。因此,滿(mǎn)足第三范式的數(shù)據(jù)庫(kù)表應(yīng)該不存在如下依賴(lài)關(guān)系:

            關(guān)鍵字段非關(guān)鍵字段x → 非關(guān)鍵字段y

            假定學(xué)生關(guān)系表為Student(學(xué)號(hào), 姓名, 年齡, 所在學(xué)院, 學(xué)院地點(diǎn), 學(xué)院電話(huà)),關(guān)鍵字為單一關(guān)鍵字"學(xué)號(hào)",因?yàn)榇嬖谌缦聸Q定關(guān)系:

            (學(xué)號(hào)) → (姓名, 年齡, 所在學(xué)院, 學(xué)院地點(diǎn), 學(xué)院電話(huà))

            這個(gè)數(shù)據(jù)庫(kù)是符合2NF的,但是不符合3NF,因?yàn)榇嬖谌缦聸Q定關(guān)系:

            (學(xué)號(hào)) → (所在學(xué)院) → (學(xué)院地點(diǎn), 學(xué)院電話(huà))

            即存在非關(guān)鍵字段"學(xué)院地點(diǎn)""學(xué)院電話(huà)"對(duì)關(guān)鍵字段"學(xué)號(hào)"的傳遞函數(shù)依賴(lài)。

            它也會(huì)存在數(shù)據(jù)冗余、更新異常、插入異常和刪除異常的情況,讀者可自行分析得知。

            把學(xué)生關(guān)系表分為如下兩個(gè)表:

            學(xué)生:(學(xué)號(hào), 姓名, 年齡, 所在學(xué)院)

            學(xué)院:(學(xué)院, 地點(diǎn), 電話(huà))

            這樣的數(shù)據(jù)庫(kù)表是符合第三范式的,消除了數(shù)據(jù)冗余、更新異常、插入異常和刪除異常。

            鮑依斯-科得范式(BCNF):在第三范式的基礎(chǔ)上,數(shù)據(jù)庫(kù)表中如果不存在任何字段對(duì)任一候選關(guān)鍵字段的傳遞函數(shù)依賴(lài)則符合第三范式。

            假設(shè)倉(cāng)庫(kù)管理關(guān)系表為StorehouseManage(倉(cāng)庫(kù)ID, 存儲(chǔ)物品ID, 管理員ID, 數(shù)量),且有一個(gè)管理員只在一個(gè)倉(cāng)庫(kù)工作;一個(gè)倉(cāng)庫(kù)可以存儲(chǔ)多種物品。這個(gè)數(shù)據(jù)庫(kù)表中存在如下決定關(guān)系:

            (倉(cāng)庫(kù)ID, 存儲(chǔ)物品ID) →(管理員ID, 數(shù)量)

            (管理員ID, 存儲(chǔ)物品ID) → (倉(cāng)庫(kù)ID, 數(shù)量)

            所以,(倉(cāng)庫(kù)ID, 存儲(chǔ)物品ID)(管理員ID, 存儲(chǔ)物品ID)都是StorehouseManage的候選關(guān)鍵字,表中的唯一非關(guān)鍵字段為數(shù)量,它是符合第三范式的。但是,由于存在如下決定關(guān)系:

            (倉(cāng)庫(kù)ID) → (管理員ID)

            (管理員ID) → (倉(cāng)庫(kù)ID)

            即存在關(guān)鍵字段決定關(guān)鍵字段的情況,所以其不符合BCNF范式。它會(huì)出現(xiàn)如下異常情況:

            (1) 刪除異常:

            當(dāng)倉(cāng)庫(kù)被清空后,所有"存儲(chǔ)物品ID""數(shù)量"信息被刪除的同時(shí),"倉(cāng)庫(kù)ID""管理員ID"信息也被刪除了。

            (2) 插入異常:

            當(dāng)倉(cāng)庫(kù)沒(méi)有存儲(chǔ)任何物品時(shí),無(wú)法給倉(cāng)庫(kù)分配管理員。

            (3) 更新異常:

            如果倉(cāng)庫(kù)換了管理員,則表中所有行的管理員ID都要修改。

            把倉(cāng)庫(kù)管理關(guān)系表分解為二個(gè)關(guān)系表:

            倉(cāng)庫(kù)管理:StorehouseManage(倉(cāng)庫(kù)ID, 管理員ID)

            倉(cāng)庫(kù):Storehouse(倉(cāng)庫(kù)ID, 存儲(chǔ)物品ID, 數(shù)量)

            這樣的數(shù)據(jù)庫(kù)表是符合BCNF范式的,消除了刪除異常、插入異常和更新異常。

          范式應(yīng)用

            我們來(lái)逐步搞定一個(gè)論壇的數(shù)據(jù)庫(kù),有如下信息:

            (1) 用戶(hù):用戶(hù)名,email,主頁(yè),電話(huà),聯(lián)系地址

            (2) 帖子:發(fā)帖標(biāo)題,發(fā)帖內(nèi)容,回復(fù)標(biāo)題,回復(fù)內(nèi)容

            第一次我們將數(shù)據(jù)庫(kù)設(shè)計(jì)為僅僅存在表:
            

          用戶(hù)名

          email

          主頁(yè)

          電話(huà)

          聯(lián)系地址

          發(fā)帖標(biāo)題

          發(fā)帖內(nèi)容

          回復(fù)標(biāo)題

          回復(fù)內(nèi)容


            這個(gè)數(shù)據(jù)庫(kù)表符合第一范式,但是沒(méi)有任何一組候選關(guān)鍵字能決定數(shù)據(jù)庫(kù)表的整行,唯一的關(guān)鍵字段用戶(hù)名也不能完全決定整個(gè)元組。我們需要增加"發(fā)帖ID""回復(fù)ID"字段,即將表修改為:

          用戶(hù)名

          email

          主頁(yè)

          電話(huà)

          聯(lián)系地址

          發(fā)帖ID

          發(fā)帖標(biāo)題

          發(fā)帖內(nèi)容

          回復(fù)ID

          回復(fù)標(biāo)題

          回復(fù)內(nèi)容


            這樣數(shù)據(jù)表中的關(guān)鍵字(用戶(hù)名,發(fā)帖ID,回復(fù)ID)能決定整行:

            (用戶(hù)名,發(fā)帖ID,回復(fù)ID) → (email,主頁(yè),電話(huà),聯(lián)系地址,發(fā)帖標(biāo)題,發(fā)帖內(nèi)容,回復(fù)標(biāo)題,回復(fù)內(nèi)容)

            但是,這樣的設(shè)計(jì)不符合第二范式,因?yàn)榇嬖谌缦聸Q定關(guān)系:

            (用戶(hù)名) → (email,主頁(yè),電話(huà),聯(lián)系地址)

            (發(fā)帖ID) → (發(fā)帖標(biāo)題,發(fā)帖內(nèi)容)

            (回復(fù)ID) → (回復(fù)標(biāo)題,回復(fù)內(nèi)容)

            即非關(guān)鍵字段部分函數(shù)依賴(lài)于候選關(guān)鍵字段,很明顯,這個(gè)設(shè)計(jì)會(huì)導(dǎo)致大量的數(shù)據(jù)冗余和操作異常。

            我們將數(shù)據(jù)庫(kù)表分解為(帶下劃線(xiàn)的為關(guān)鍵字):

            (1) 用戶(hù)信息:用戶(hù)名,email,主頁(yè),電話(huà),聯(lián)系地址

            (2) 帖子信息:發(fā)帖ID,標(biāo)題,內(nèi)容

            (3) 回復(fù)信息:回復(fù)ID,標(biāo)題,內(nèi)容

            (4) 發(fā)貼:用戶(hù)名,發(fā)帖ID

            (5) 回復(fù):發(fā)帖ID,回復(fù)ID

            這樣的設(shè)計(jì)是滿(mǎn)足第123范式和BCNF范式要求的,但是這樣的設(shè)計(jì)是不是最好的呢?

            不一定。

             觀(guān)察可知,第4項(xiàng)"發(fā)帖"中的"用戶(hù)名""發(fā)帖ID"之間是1N的關(guān)系,因此我們可以把"發(fā)帖"合并到第2項(xiàng)的"帖子信息"中;第5項(xiàng)"回復(fù)"中的 "發(fā)帖ID""回復(fù)ID"之間也是1N的關(guān)系,因此我們可以把"回復(fù)"合并到第3項(xiàng)的"回復(fù)信息"中。這樣可以一定量地減少數(shù)據(jù)冗余,新的設(shè)計(jì)為:

            (1) 用戶(hù)信息:用戶(hù)名,email,主頁(yè),電話(huà),聯(lián)系地址

            (2) 帖子信息:用戶(hù)名,發(fā)帖ID,標(biāo)題,內(nèi)容

            (3) 回復(fù)信息:發(fā)帖ID,回復(fù)ID,標(biāo)題,內(nèi)容

            數(shù)據(jù)庫(kù)表1顯然滿(mǎn)足所有范式的要求;

            數(shù)據(jù)庫(kù)表2中存在非關(guān)鍵字段"標(biāo)題""內(nèi)容"對(duì)關(guān)鍵字段"發(fā)帖ID"的部分函數(shù)依賴(lài),即不滿(mǎn)足第二范式的要求,但是這一設(shè)計(jì)并不會(huì)導(dǎo)致數(shù)據(jù)冗余和操作異常;

            數(shù)據(jù)庫(kù)表3中也存在非關(guān)鍵字段"標(biāo)題""內(nèi)容"對(duì)關(guān)鍵字段"回復(fù)ID"的部分函數(shù)依賴(lài),也不滿(mǎn)足第二范式的要求,但是與數(shù)據(jù)庫(kù)表2相似,這一設(shè)計(jì)也不會(huì)導(dǎo)致數(shù)據(jù)冗余和操作異常。

            由此可以看出,并不一定要強(qiáng)行滿(mǎn)足范式的要求,對(duì)于1N關(guān)系,當(dāng)1的一邊合并到N的那邊后,N的那邊就不再滿(mǎn)足第二范式了,但是這種設(shè)計(jì)反而比較好!

            對(duì)于MN的關(guān)系,不能將M一邊或N一邊合并到另一邊去,這樣會(huì)導(dǎo)致不符合范式要求,同時(shí)導(dǎo)致操作異常和數(shù)據(jù)冗余。
          對(duì)于11的關(guān)系,我們可以將左邊的1或者右邊的1合并到另一邊去,設(shè)計(jì)導(dǎo)致不符合范式要求,但是并不會(huì)導(dǎo)致操作異常和數(shù)據(jù)冗余。

            結(jié)論

            滿(mǎn)足范式要求的數(shù)據(jù)庫(kù)設(shè)計(jì)是結(jié)構(gòu)清晰的,同時(shí)可避免數(shù)據(jù)冗余和操作異常。這并意味著不符合范式要求的設(shè)計(jì)一定是錯(cuò)誤的,在數(shù)據(jù)庫(kù)表中存在111N關(guān)系這種較特殊的情況下,合并導(dǎo)致的不符合范式要求反而是合理的。

            在我們?cè)O(shè)計(jì)數(shù)據(jù)庫(kù)的時(shí)候,一定要時(shí)刻考慮范式的要求。

          posted @ 2005-12-20 10:54 DenisLing 閱讀(1816) | 評(píng)論 (3)編輯 收藏

          2005年12月19日 #

          對(duì)spring事務(wù)類(lèi)型詳解的一點(diǎn)補(bǔ)充(關(guān)于嵌套事務(wù))

               摘要: 可能大家對(duì)PROPAGATION_NESTED還不怎么了解,覺(jué)得有必要再補(bǔ)充一下^_^!  閱讀全文

          posted @ 2005-12-19 16:02 DenisLing 閱讀(3227) | 評(píng)論 (5)編輯 收藏

          2005年12月18日 #

          Spring事務(wù)類(lèi)型祥解

               摘要: 大家可能在spring中經(jīng)常看到這樣的定義:


          PROPAGATION_REQUIRED,readOnlyPROPAGATION_REQUIRED

          估計(jì)有好多朋友還沒(méi)有弄清楚里面的值的意思,仔細(xì)看完下面應(yīng)該知道自己什么情況下面應(yīng)該使用什么樣的聲明。^_^  閱讀全文

          posted @ 2005-12-18 16:30 DenisLing 閱讀(9641) | 評(píng)論 (2)編輯 收藏

          2005年12月1日 #

          spring聲明式事務(wù)管理祥述

          Spring也提供了聲明式事務(wù)管理。這是通過(guò)Spring AOP實(shí)現(xiàn)的。

          Spring 中進(jìn)行事務(wù)管理的通常方式是利用AOP(面向切片編程)的方式,為普通java類(lèi)封裝事務(wù)控制,它是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的,由于接口是延遲實(shí)例化的, spring在這段時(shí)間內(nèi)通過(guò)攔截器,加載事務(wù)切片。原理就是這樣,具體細(xì)節(jié)請(qǐng)參考jdk中有關(guān)動(dòng)態(tài)代理的文檔。本文主要講解如何在spring中進(jìn)行事 務(wù)控制。
          動(dòng)態(tài)代理的一個(gè)重要特征是,它是針對(duì)接口的,所以我們的dao要通過(guò)動(dòng)態(tài)代理來(lái)讓spring接管事務(wù),就必須在dao前面抽象出一個(gè)接口,當(dāng)然如果沒(méi)有這樣的接口,那么spring會(huì)使用CGLIB來(lái)解決問(wèn)題,但這不是spring推薦的方式,所以不做討論.

          大多數(shù)Spring用戶(hù)選擇聲明式事務(wù)管理。這是最少影響應(yīng)用代碼的選擇, 因而這是和非侵入性的輕量級(jí)容器的觀(guān)念是一致的。

          從考慮EJB CMT和Spring聲明式事務(wù)管理的相似以及不同之處出發(fā)是很有益的。 它們的基本方法是相似的:都可以指定事務(wù)管理到單獨(dú)的方法;如果需要可以在事務(wù)上 下文調(diào)用setRollbackOnly()方法。不同之處如下:

          • 不象EJB CMT綁定在JTA上,Spring聲明式事務(wù)管理可以在任何環(huán)境下使用。 只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事務(wù)機(jī)制一起工作

          • Spring可以使聲明式事務(wù)管理應(yīng)用到普通Java對(duì)象,不僅僅是特殊的類(lèi),如EJB

          • Spring提供聲明式回滾規(guī)則:EJB沒(méi)有對(duì)應(yīng)的特性, 我們將在下面討論這個(gè)特性。回滾可以聲明式控制,不僅僅是編程式的

          • Spring允許你通過(guò)AOP定制事務(wù)行為。例如,如果需要,你可以在事務(wù) 回滾中插入定制的行為。你也可以增加任意的通知,就象事務(wù)通知一樣。使用 EJB CMT,除了使用setRollbackOnly(),你沒(méi)有辦法能 夠影響容器的事務(wù)管理

          • Spring不提供高端應(yīng)用服務(wù)器提供的跨越遠(yuǎn)程調(diào)用的事務(wù)上下文傳播。如 果你需要這些特性,我們推薦你使用EJB。然而,不要輕易使用這些特性。通常我 們并不希望事務(wù)跨越遠(yuǎn)程調(diào)用

          回滾規(guī)則的概念是很重要的:它們使得我們可以指定哪些異常應(yīng)該發(fā)起自 動(dòng)回滾。我們?cè)谂渲梦募校皇荍ava代碼中,以聲明的方式指定。因此,雖然我們?nèi)?然可以編程調(diào)用TransactionStatus對(duì)象的 setRollbackOnly()方法來(lái)回滾當(dāng)前事務(wù),多數(shù)時(shí)候我們可以 指定規(guī)則,如MyApplicationException應(yīng)該導(dǎo)致回滾。 這有顯著的優(yōu)點(diǎn),業(yè)務(wù)對(duì)象不需要依賴(lài)事務(wù)基礎(chǔ)設(shè)施。例如,它們通常不需要引 入任何Spring API,事務(wù)或其他任何東西。

          EJB的默認(rèn)行為是遇到系統(tǒng)異常(通常是運(yùn)行時(shí)異常), EJB容器自動(dòng)回滾事務(wù)。EJB CMT遇到應(yīng)用程序異常 (除了java.rmi.RemoteException外的checked異常)時(shí)不 會(huì)自動(dòng)回滾事務(wù)。雖然Spring聲明式事務(wù)管理沿用EJB的約定(遇到unchecked 異常自動(dòng)回滾事務(wù)),但是這是可以定制的。

          按照我們的測(cè)試,Spring聲明式事務(wù)管理的性能要?jiǎng)龠^(guò)EJB CMT。

          通 常通過(guò)TransactionProxyFactoryBean設(shè)置Spring事務(wù)代理。我們需 要一個(gè)目標(biāo)對(duì)象包裝在事務(wù)代理中。這個(gè)目標(biāo)對(duì)象一般是一個(gè)普通Java對(duì)象的bean。當(dāng)我 們定義TransactionProxyFactoryBean時(shí),必須提供一個(gè)相關(guān)的 PlatformTransactionManager的引用和事務(wù)屬性事務(wù)屬性含有上面描述的事務(wù)定義。

          <bean id="petStore" 
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          <property name="transactionManager"><ref bean="transactionManager"/></property>
          <property name="target"><ref bean="petStoreTarget"/></property>
          <property name="transactionAttributes">
          <props>
          <prop key="insert*">PROPAGATION_REQUIRED,-MyCheckedException</prop>
          <prop key="update*">PROPAGATION_REQUIRED</prop>
          <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
          </props>
          </property>
          </bean>

          事 務(wù)代理會(huì)實(shí)現(xiàn)目標(biāo)對(duì)象的接口:這里是id為petStoreTarget的bean。(使用 CGLIB也可以實(shí)現(xiàn)具體類(lèi)的代理。只要設(shè)置proxyTargetClass屬性為true就可以。 如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)任何接口,這將自動(dòng)設(shè)置該屬性為true。通常,我們希望面向接口而不是 類(lèi)編程。)使用proxyInterfaces屬性來(lái)限定事務(wù)代理來(lái)代 理指定接口也是可以的(一般來(lái)說(shuō)是個(gè)好想法)。也可以通過(guò)從 org.springframework.aop.framework.ProxyConfig繼承或所有AOP代理工廠(chǎng)共享 的屬性來(lái)定制TransactionProxyFactoryBean的行為。

          這里的transactionAttributes屬性定義在 org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource 中的屬性格式來(lái)設(shè)置。這個(gè)包括通配符的方法名稱(chēng)映射是很直觀(guān)的。注意 insert*的映射的值包括回滾規(guī)則。添加的-MyCheckedException 指定如果方法拋出MyCheckedException或它的子類(lèi),事務(wù)將 會(huì)自動(dòng)回滾。可以用逗號(hào)分隔定義多個(gè)回滾規(guī)則。-前綴強(qiáng)制回滾,+前綴指定提交(這允許即使拋出unchecked異常時(shí)也可以提交事務(wù),當(dāng)然你自己要明白自己 在做什么)。

          TransactionProxyFactoryBean允許你通過(guò) “preInterceptors”和“postInterceptors”屬性設(shè)置“前”或“后”通知來(lái)提供額外的 攔截行為。可以設(shè)置任意數(shù)量的“前”和“后”通知,它們的類(lèi)型可以是 Advisor(可以包含一個(gè)切入點(diǎn)), MethodInterceptor或被當(dāng)前Spring配置支持的通知類(lèi)型 (例如ThrowAdviceAfterReturningtAdviceBeforeAdvice, 這些都是默認(rèn)支持的)。這些通知必須支持實(shí)例共享模式。如果你需要高級(jí)AOP特 性來(lái)使用事務(wù),如有狀態(tài)的maxin,那最好使用通用的 org.springframework.aop.framework.ProxyFactoryBean, 而不是TransactionProxyFactoryBean實(shí)用代理創(chuàng)建者。

          也可以設(shè)置自動(dòng)代理:配置AOP框架,不需要單獨(dú)的代理定義類(lèi)就可以生成類(lèi)的 代理。

          附兩個(gè)spring的事務(wù)配置例子:
          <prop key="add">
               PROPAGATION_REQUIRES_NEW, -MyException
          </prop>
          注:上面的意思是add方法將獨(dú)占一個(gè)事務(wù),當(dāng)事務(wù)處理過(guò)程中產(chǎn)生MyException異常或者該異常的子類(lèi)將回滾該事務(wù)。

          <prop key="loadAll">
              PROPAGATION_SUPPORTS, ISOLATION_READ_COMMITED, Readonly
          </prop>
          注:表示loadAll方法支持事務(wù),而且不會(huì)讀取沒(méi)有提交事務(wù)的數(shù)據(jù)。它的數(shù)據(jù)為只讀(這樣有助于提高讀取的性能)

          附A Spring中的所有事務(wù)策略

          PROPAGATION_MANDATORY
          PROPAGATION_NESTED
          PROPAGATION_NEVER
          PROPAGATION_NOT_SUPPORTED
          PROPAGATION_REQUIRED
          PROPAGATION_REQUIRED_NEW
          PROPAGATION_SUPPORTS

          附B Spring中所有的隔離策略:

          ISOLATION_DEFAULT
          ISOLATION_READ_UNCOMMITED
          ISOLATION_COMMITED
          ISOLATION_REPEATABLE_READ
          ISOLATION_SERIALIZABLE

          posted @ 2005-12-01 11:47 DenisLing 閱讀(15363) | 評(píng)論 (0)編輯 收藏

          2005年11月28日 #

          webwork開(kāi)發(fā)團(tuán)隊(duì)加入Struts(吃驚)

          早上一過(guò)來(lái)看到下面的新聞感覺(jué)驚訝的要死!不知道和我一樣在struts和webwork之間一直在徘徊的朋友們看了由什么感覺(jué)。本來(lái)我的態(tài)度是想慢慢放棄struts的…… 看樣子要重新看待struts了。原文如下:

          WebWork joining Struts


          Yes, . The WebWork development team (Jason and I) have been working with the Struts development team (Don Brown and Ted Husted) and have come to the conclusion that the best thing for Java community would be to merge WebWork in to Struts.

          Read Ted's email here, but the gist of it is this: WebWork is a great technology, and Struts is a great community. It's a perfect match and bringing the two together will only be better for WebWork and Struts users alike. The only down side for me is that I'll be working less with OpenSymphony, but I believe that is a small price for all the great benefits that come from this merger.

          Just to be clear, WebWork is not going away. WebWork 2.2 is still coming out any day now, and there may even be a WebWork 2.3. But new minor/major versions won't be coming out under the WebWork name for much longer. Instead, they will be released under the Struts incubator program with the intent to eventually become Struts Action Framework 2.0.

          So don't worry, WebWork 2.1.8, 2.2.1, and other bug fix releases will continue to come out and we will support the WebWork community as long as needed. In addition, we'll make compatibility with both Struts and WebWork a high priority, so future releases may help with that. To be clear: no one is leaving WebWork and it will continue to be supported for a very long time.

          With this renewed energy, larger development team, and larger community, the combined efforts of Struts and WebWork will surely make the Struts platform the easiest, fastest, and most powerful Java web framework available. We hope that all the WebWork users and developers are as excited about this as we are and are ready to take WebWork to the next level.

          原文地址:http://www.opensymphony.com/webwork/

          以前一直在struts和webwork之間猶豫,看來(lái)struts氣數(shù)未盡呀。apache組織還是比較只值得信賴(lài)的。呵呵!

          posted @ 2005-11-28 08:47 DenisLing 閱讀(1004) | 評(píng)論 (6)編輯 收藏

          2005年11月21日 #

          自定義標(biāo)簽獲取struts中action的全部路徑(原創(chuàng))

                由于項(xiàng)目里面由需要一個(gè)form可以提交多個(gè)action(本來(lái)可以用 dispatch值來(lái)實(shí)現(xiàn),后來(lái)考慮到要使用validator框架驗(yàn)證)。后來(lái)考慮的方案為使用js來(lái)控制form的流向,例如
                
          form.action='/bookstore/checkId.do'

          不過(guò)新的問(wèn)題來(lái)了!如何能不用hardcode而拿到我要的action的實(shí)際路徑呢?比如我定義的struts-config文件里面的action是 
          path="/checkId"
          但是實(shí)際解釋后的path是:
          action='/bookstore/checkId.do'
          前 綴和后面的后綴.do都是根據(jù)你的項(xiàng)目部署的路徑和你在web.xml中配置的mapping的后綴有關(guān)系,如果我把內(nèi)容寫(xiě)死到j(luò)sp中那以后我要是想把 checkId.do改成checkId.action那就要更改jsp,由于struts本來(lái)提供的幾個(gè)taglib里面的

          <html:form action="/checkId" >

          綜合了一下決定還是自己寫(xiě)個(gè)taglib來(lái)實(shí)現(xiàn),其實(shí)只需要照著struts 中的 FormTag.java 文件依葫蘆畫(huà)瓢就可以了,一下為本人的代碼部分

          StrutsActionCustomTag.java
          package com.denis.framework.common.taglib.strutsActionExt;

          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.jsp.JspException;
          import javax.servlet.jsp.tagext.TagSupport;

          import org.apache.struts.taglib.TagUtils;

          public class StrutsActionCustomTag extends TagSupport {

             
          protected String actionName = null;
             
              
          public String getActionName() {
                 
          return actionName;
              }


              
          public void setActionName(String actionName) {
                 
          this.actionName = actionName;
              }


              
          public int doStartTag() throws JspException {

                  StringBuffer results 
          = new StringBuffer();
                  HttpServletResponse response 
          =
                      (HttpServletResponse) 
          this.pageContext.getResponse();
                 
                  results.append(response.encodeURL( TagUtils.getInstance().getActionMappingURL( 
          this.actionName , this.pageContext)) );

                  TagUtils.getInstance().write(pageContext, results.toString());

                 
          return (EVAL_BODY_INCLUDE);
              }


              
          public void release() {
                 
          super.release();
                 
          this.actionName = null ;

              }

          }


          tld定義部分

          framework-struts.tld
          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
          <taglib>

          <tlibversion>1.0</tlibversion>
              <jspversion>1.1</jspversion>
              <shortname>framework</shortname>
              <uri>http://www.denisFramework.org/Framework-Tag</uri>
              <tag>
              <name>getActionUrl</name>
              <tagclass>com.denis.framework.common.taglib.strutsActionExt.StrutsActionCustomTag</tagclass>
              <bodycontent>empty</bodycontent>
              <attribute>
                   <name>actionName</name>
                   <required>true</required>
                   <rtexprvalue>true</rtexprvalue>
              </attribute>
          </tag>

          </taglib>



          ok ! 直接在jsp中如下使用即可取到action的真正路徑

           
          <%@ page language="java"%>
          <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%> 
          <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
          <%@ taglib uri="/WEB-INF/framework-struts.tld" prefix="framework" %>
           
          <html> 
             
          <head>
                 
          <title>JSP for loginForm form</title>
             
          </head>
             
          <body>
                 
          <framework:getActionUrl actionName="login" />
                 
          <html:form action="/login">
                      name : 
          <html:text property="name"/><html:errors property="name"/><br/>
                      password : 
          <html:password property="password"/><html:errors property="password"/><br/>
                     
          <html:submit/><html:cancel/>
                 
          </html:form>
             
          </body>
          </html>



          大家要是有更好的解決方法希望能指正!謝謝!

          posted @ 2005-11-21 15:52 DenisLing 閱讀(1870) | 評(píng)論 (1)編輯 收藏

          使用 Spring 更好地處理 Struts 動(dòng)作 (轉(zhuǎn)自ibm 個(gè)人感覺(jué)受益非淺)

          Struts Recipes 的合著者 George Franciscus 將介紹另一個(gè)重大的 Struts 整合竅門(mén) —— 這次是將 Struts 應(yīng)用程序?qū)?Spring 框架。請(qǐng)跟隨 George,他將向您展示如何改變 Struts 動(dòng)作,使得管理 Struts 動(dòng)作就像管理 Spring beans 那樣。結(jié)果是一個(gè)增強(qiáng)的 web 框架,這個(gè)框架可以方便地利用 Spring AOP 的優(yōu)勢(shì)。

          您肯定已經(jīng)聽(tīng)說(shuō)過(guò)控制反轉(zhuǎn) (IOC) 設(shè)計(jì)模式,因?yàn)楹荛L(zhǎng)一段時(shí)間以來(lái)一直在流傳關(guān)于它的信息。如果您在任何功能中使用過(guò) Spring 框架,那么您就知道其原理的作用。在本文中,我利用這一原理把一個(gè) Struts 應(yīng)用程序注入 Spring 框架,您將親身體會(huì)到 IOC 模式的強(qiáng)大。

          將一個(gè) Struts 應(yīng)用程序整合進(jìn) Spring 框架具有多方面的優(yōu)點(diǎn)。首先,Spring 是為解決一些關(guān)于 JEE 的真實(shí)世界問(wèn)題而設(shè)計(jì)的,比如復(fù)雜性、低性能和可測(cè)試性,等等。第二,Spring 框架包含一個(gè) AOP 實(shí)現(xiàn),允許您將面向方面技術(shù)應(yīng)用于面向?qū)ο蟮拇a。第三,一些人可能會(huì)說(shuō) Spring 框架只有處理 Struts 比 Struts 處理自己好。但是這是觀(guān)點(diǎn)問(wèn)題,我演示三種將 Struts 應(yīng)用程序整合到 Spring 框架的方法后,具體由您自己決定使用哪一種。

          我所演示的方法都是執(zhí)行起來(lái)相對(duì)簡(jiǎn)單的,但是它們卻具有明顯不同的優(yōu)點(diǎn)。我為每一種方法創(chuàng)建了一個(gè)獨(dú)立而可用的例子,這樣您就可以完全理解每種方法。 請(qǐng)參閱 下載 部分獲得完整例子源代碼。請(qǐng)參閱 參考資料,下載 Struts MVC 和 Spring 框架。

          為什么 Spring 這么了不起?

          Spring 的創(chuàng)立者 Rod Johnson 以一種批判的眼光看待 Java? 企業(yè)軟件開(kāi)發(fā),并且提議很多企業(yè)難題都能夠通過(guò)戰(zhàn)略地使用 IOC 模式(也稱(chēng)作依賴(lài)注入)來(lái)解決。當(dāng) Rod 和一個(gè)具有奉獻(xiàn)精神的開(kāi)放源碼開(kāi)發(fā)者團(tuán)隊(duì)將這個(gè)理論應(yīng)用于實(shí)踐時(shí),結(jié)果就產(chǎn)生了 Spring 框架。簡(jiǎn)言之,Spring 是一個(gè)輕型的容器,利用它可以使用一個(gè)外部 XML 配置文件方便地將對(duì)象連接在一起。每個(gè)對(duì)象都可以通過(guò)顯示一個(gè) JavaBean 屬性收到一個(gè)到依賴(lài)對(duì)象的引用,留給您的簡(jiǎn)單任務(wù)就只是在一個(gè) XML 配置文件中把它們連接好。

          IOC 和 Spring

          IOC 是一種使應(yīng)用程序邏輯外在化的設(shè)計(jì)模式,所以它是被注入而不是被寫(xiě)入客戶(hù)機(jī)代碼中。將 IOC 與接口編程應(yīng)用結(jié)合,就像 Spring 框架那樣,產(chǎn)生了一種架構(gòu),這種架構(gòu)能夠減少客戶(hù)機(jī)對(duì)特定實(shí)現(xiàn)邏輯的依賴(lài)。請(qǐng)參閱 參考資料 了解更多關(guān)于 IOC 和 Spring 的信息。

          依 賴(lài)注入是一個(gè)強(qiáng)大的特性,但是 Spring 框架能夠提供更多特性。Spring 支持可插拔的事務(wù)管理器,可以給您的事務(wù)處理提供更廣泛的選擇范圍。它集成了領(lǐng)先的持久性框架,并且提供一個(gè)一致的異常層次結(jié)構(gòu)。Spring 還提供了一種使用面向方面代碼代替正常的面向?qū)ο蟠a的簡(jiǎn)單機(jī)制。

          Spring AOP 允許您使用攔截器 在一個(gè)或多個(gè)執(zhí)行點(diǎn)上攔截應(yīng)用程序邏輯。加強(qiáng)應(yīng)用程序在攔截器中的日志記錄邏輯會(huì)產(chǎn)生一個(gè)更可讀的、實(shí)用的代碼基礎(chǔ),所以攔截器廣泛用于日志記錄。您很快就會(huì)看到,為了處理橫切關(guān)注點(diǎn),Spring AOP 發(fā)布了它自己的攔截器,您也可以編寫(xiě)您自己的攔截器。





          整合 Struts 和 Spring

          與 Struts 相似,Spring 可以作為一個(gè) MVC 實(shí)現(xiàn)。這兩種框架都具有自己的優(yōu)點(diǎn)和缺點(diǎn),盡管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多開(kāi)發(fā)團(tuán)隊(duì)已經(jīng)學(xué)會(huì)在時(shí)間緊迫的時(shí)候利用 Struts 作為構(gòu)造高品質(zhì)軟件的基礎(chǔ)。Struts 具有如此大的推動(dòng)力,以至于開(kāi)發(fā)團(tuán)隊(duì)寧愿整合 Spring 框架的特性,而不愿意轉(zhuǎn)換成 Spring MVC。沒(méi)必要進(jìn)行轉(zhuǎn)換對(duì)您來(lái)說(shuō)是一個(gè)好消息。Spring 架構(gòu)允許您將 Struts 作為 Web 框架連接到基于 Spring 的業(yè)務(wù)和持久層。最后的結(jié)果就是現(xiàn)在一切條件都具備了。

          在接下來(lái)的小竅門(mén)中,您將會(huì)了解到三種將 Struts MVC 整合到 Spring 框架的方法。我將揭示每種方法的缺陷并且對(duì)比它們的優(yōu)點(diǎn)。 一旦您了解到所有三種方法的作用,我將會(huì)向您展示一個(gè)令人興奮的應(yīng)用程序,這個(gè)程序使用的是這三種方法中我最喜歡的一種。





          三個(gè)小竅門(mén)

          接下來(lái)的每種整合技術(shù)(或者竅門(mén))都有自己的優(yōu)點(diǎn)和特點(diǎn)。我偏愛(ài)其中的一種,但是我知道這三種都能夠加深您對(duì) Struts 和 Spring 的理解。在處理各種不同情況的時(shí)候,這將給您提供一個(gè)廣闊的選擇范圍。方法如下:

          • 使用 Spring 的 ActionSupport 類(lèi)整合 Structs
          • 使用 Spring 的 DelegatingRequestProcessor 覆蓋 Struts 的 RequestProcessor
          • 將 Struts Action 管理委托給 Spring 框架

          裝載應(yīng)用程序環(huán)境

          無(wú)論您使用哪種技術(shù),都需要使用 Spring 的 ContextLoaderPlugin 為 Struts 的 ActionServlet 裝載 Spring 應(yīng)用程序環(huán)境。就像添加任何其他插件一樣,簡(jiǎn)單地向您的 struts-config.xml 文件添加該插件,如下所示:


          <plug-in className=
          "org.springframework.web.struts.ContextLoaderPlugIn">
          <set-property property=
          "contextConfigLocation" value="/WEB-INF/beans.xml"/>
          </plug-in>


          前面已經(jīng)提到過(guò),在 下載 部分,您能夠找到這三個(gè)完全可使用的例子的完整源代碼。每個(gè)例子都為一個(gè)書(shū)籍搜索應(yīng)用程序提供一種不同的 Struts 和 Spring 的整合方法。您可以在這里看到例子的要點(diǎn),但是您也可以下載應(yīng)用程序以查看所有的細(xì)節(jié)。





          竅門(mén) 1. 使用 Spring 的 ActionSupport

          手動(dòng)創(chuàng)建一個(gè) Spring 環(huán)境是一種整合 Struts 和 Spring 的最直觀(guān)的方式。為了使它變得更簡(jiǎn)單,Spring 提供了一些幫助。為了方便地獲得 Spring 環(huán)境,org.springframework.web.struts.ActionSupport 類(lèi)提供了一個(gè) getWebApplicationContext() 方法。您所做的只是從 Spring 的 ActionSupport 而不是 Struts Action 類(lèi)擴(kuò)展您的動(dòng)作,如清單 1 所示:


          清單 1. 使用 ActionSupport 整合 Struts

          package ca.nexcel.books.actions;

          import java.io.IOException;

          import javax.servlet.ServletException;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          import org.apache.struts.action.ActionError;
          import org.apache.struts.action.ActionErrors;
          import org.apache.struts.action.ActionForm;
          import org.apache.struts.action.ActionForward;
          import org.apache.struts.action.ActionMapping;
          import org.apache.struts.action.DynaActionForm;
          import org.springframework.context.ApplicationContext;
          import org.springframework.web.struts.ActionSupport;

          import ca.nexcel.books.beans.Book;
          import ca.nexcel.books.business.BookService;

          public class SearchSubmit extends ActionSupport { |(1)


          public ActionForward execute(
          ActionMapping mapping,
          ActionForm form,
          HttpServletRequest request,
          HttpServletResponse response)
          throws IOException, ServletException {

          DynaActionForm searchForm = (DynaActionForm) form;
          String isbn = (String) searchForm.get("isbn");

          //the old fashion way
          //BookService bookService = new BookServiceImpl();

          ApplicationContext ctx =
          getWebApplicationContext(); |(2)
          BookService bookService =
          (BookService) ctx.getBean("bookService"); |(3)

          Book book = bookService.read(isbn.trim());

          if (null == book) {
          ActionErrors errors = new ActionErrors();
          errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
          ("message.notfound"));
          saveErrors(request, errors);
          return mapping.findForward("failure") ;
          }

          request.setAttribute("book", book);
          return mapping.findForward("success");
          }
          }


          讓我們快速思考一下這里到底發(fā)生了什么。在 (1) 處,我通過(guò)從 Spring 的 ActionSupport 類(lèi)而不是 Struts 的 Action 類(lèi)進(jìn)行擴(kuò)展,創(chuàng)建了一個(gè)新的 Action在 (2) 處,我使用 getWebApplicationContext() 方法獲得一個(gè) ApplicationContext為了獲得業(yè)務(wù)服務(wù),我使用在 (2) 處獲得的環(huán)境在 (3) 處查找一個(gè) Spring bean。

          這 種技術(shù)很簡(jiǎn)單并且易于理解。不幸的是,它將 Struts 動(dòng)作與 Spring 框架耦合在一起。如果您想替換掉 Spring,那么您必須重寫(xiě)代碼。并且,由于 Struts 動(dòng)作不在 Spring 的控制之下,所以它不能獲得 Spring AOP 的優(yōu)勢(shì)。當(dāng)使用多重獨(dú)立的 Spring 環(huán)境時(shí),這種技術(shù)可能有用,但是在大多數(shù)情況下,這種方法不如另外兩種方法合適。





          竅門(mén) 2. 覆蓋 RequestProcessor

          將 Spring 從 Struts 動(dòng)作中分離是一個(gè)更巧妙的設(shè)計(jì)選擇。分離的一種方法是使用 org.springframework.web.struts.DelegatingRequestProcessor 類(lèi)來(lái)覆蓋 Struts 的 RequestProcessor 處理程序,如清單 2 所示:


          清單 2. 通過(guò) Spring 的 DelegatingRequestProcessor 進(jìn)行整合

          <?xml version="1.0" encoding="ISO-8859-1" ?>

          <!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

          <struts-config>
          <form-beans>
          <form-bean name="searchForm"
          type="org.apache.struts.validator.DynaValidatorForm">
          <form-property name="isbn" type="java.lang.String"/>
          </form-bean>

          </form-beans>

          <global-forwards type="org.apache.struts.action.ActionForward">
          <forward name="welcome" path="/welcome.do"/>
          <forward name="searchEntry" path="/searchEntry.do"/>
          <forward name="searchSubmit" path="/searchSubmit.do"/>
          </global-forwards>

          <action-mappings>
          <action path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
          <action path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
          <action path="/searchSubmit"
          type="ca.nexcel.books.actions.SearchSubmit"
          input="/searchEntry.do"
          validate="true"
          name="searchForm">
          <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
          <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
          </action>

          </action-mappings>

          <message-resources parameter="ApplicationResources"/>

          <controller processorClass="org.springframework.web.struts.
          DelegatingRequestProcessor"/> |(1)

          <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
          <set-property property="pathnames"
          value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
          </plug-in>


          <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
          <set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/>
          </plug-in>

          </struts-config>


          我利用了 <controller> 標(biāo)記來(lái)用 DelegatingRequestProcessor 覆蓋默認(rèn)的 Struts RequestProcessor下一步是在我的 Spring 配置文件中注冊(cè)該動(dòng)作,如清單 3 所示:


          清單 3. 在 Spring 配置文件中注冊(cè)一個(gè)動(dòng)作

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
          "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>
          <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>

          <bean name="/searchSubmit"
          class="ca.nexcel.books.actions.SearchSubmit"> |(1)
          <property name="bookService">
          <ref bean="bookService"/>
          </property>
          </bean>
          </beans>


          注意:在 (1) 處,我使用名稱(chēng)屬性注冊(cè)了一個(gè) bean,以匹配 struts-config 動(dòng)作映射名稱(chēng)。SearchSubmit 動(dòng)作揭示了一個(gè) JavaBean 屬性,允許 Spring 在運(yùn)行時(shí)填充屬性,如清單 4 所示:


          清單 4. 具有 JavaBean 屬性的 Struts 動(dòng)作

          package ca.nexcel.books.actions;

          import java.io.IOException;

          import javax.servlet.ServletException;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          import org.apache.struts.action.Action;
          import org.apache.struts.action.ActionError;
          import org.apache.struts.action.ActionErrors;
          import org.apache.struts.action.ActionForm;
          import org.apache.struts.action.ActionForward;
          import org.apache.struts.action.ActionMapping;
          import org.apache.struts.action.DynaActionForm;

          import ca.nexcel.books.beans.Book;
          import ca.nexcel.books.business.BookService;

          public class SearchSubmit extends Action {

          private BookService bookService;
          public BookService getBookService() {
          return bookService;
          }

          public void setBookService(BookService bookService) { | (1)
          this.bookService = bookService;
          }

          public ActionForward execute(
          ActionMapping mapping,
          ActionForm form,
          HttpServletRequest request,
          HttpServletResponse response)
          throws IOException, ServletException {

          DynaActionForm searchForm = (DynaActionForm) form;
          String isbn = (String) searchForm.get("isbn");

          Book book = getBookService().read(isbn.trim()); |(2)

          if (null == book) {
          ActionErrors errors = new ActionErrors();
          errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound"));
          saveErrors(request, errors);
          return mapping.findForward("failure") ;
          }

          request.setAttribute("book", book);
          return mapping.findForward("success");
          }

          }


          在清單 4 中,您可以了解到如何創(chuàng)建 Struts 動(dòng)作。在 (1) 處,我創(chuàng)建了一個(gè) JavaBean 屬性。DelegatingRequestProcessor自 動(dòng)地配置這種屬性。這種設(shè)計(jì)使 Struts 動(dòng)作并不知道它正被 Spring 管理,并且使您能夠利用 Sping 的動(dòng)作管理框架的所有優(yōu)點(diǎn)。由于您的 Struts 動(dòng)作注意不到 Spring 的存在,所以您不需要重寫(xiě)您的 Struts 代碼就可以使用其他控制反轉(zhuǎn)容器來(lái)替換掉 Spring。

          DelegatingRequestProcessor 方法的確比第一種方法好,但是仍然存在一些問(wèn)題。如果您使用一個(gè)不同的 RequestProcessor則需要手動(dòng)整合 Spring 的 DelegatingRequestProcessor添加的代碼會(huì)造成維護(hù)的麻煩并且將來(lái)會(huì)降低您的應(yīng)用程序的靈活性。此外,還有過(guò)一些使用一系列命令來(lái)代替 Struts RequestProcessor 的傳聞。 這種改變將會(huì)對(duì)這種解決方法的使用壽命造成負(fù)面的影響。





          竅門(mén) 3. 將動(dòng)作管理委托給 Spring

          一個(gè)更好的解決方法是將 Strut 動(dòng)作管理委托給 Spring。您可以通過(guò)在 struts-config 動(dòng)作映射中注冊(cè)一個(gè)代理來(lái)實(shí)現(xiàn)。代理負(fù)責(zé)在 Spring 環(huán)境中查找 Struts 動(dòng)作。由于動(dòng)作在 Spring 的控制之下,所以它可以填充動(dòng)作的 JavaBean 屬性,并為應(yīng)用諸如 Spring 的 AOP 攔截器之類(lèi)的特性帶來(lái)了可能。

          清單 5 中的 Action 類(lèi)與清單 4 中的相同。但是 struts-config 有一些不同:


          清單 5. Spring 整合的委托方法

          <?xml version="1.0" encoding="ISO-8859-1" ?>

          <!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

          <struts-config>
          <form-beans>
          <form-bean name="searchForm"
          type="org.apache.struts.validator.DynaValidatorForm">
          <form-property name="isbn" type="java.lang.String"/>
          </form-bean>

          </form-beans>

          <global-forwards type="org.apache.struts.action.ActionForward">
          <forward name="welcome" path="/welcome.do"/>
          <forward name="searchEntry" path="/searchEntry.do"/>
          <forward name="searchSubmit" path="/searchSubmit.do"/>
          </global-forwards>

          <action-mappings>
          <action path="/welcome" forward="/WEB-INF/pages/welcome.htm"/>
          <action path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/>
          <action path="/searchSubmit"
          type="org.springframework.web.struts.DelegatingActionProxy" |(1)
          input="/searchEntry.do"
          validate="true"
          name="searchForm">
          <forward name="success" path="/WEB-INF/pages/detail.jsp"/>
          <forward name="failure" path="/WEB-INF/pages/search.jsp"/>
          </action>

          </action-mappings>

          <message-resources parameter="ApplicationResources"/>


          <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
          <set-property
          property="pathnames"
          value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
          </plug-in>


          <plug-in
          className="org.springframework.web.struts.ContextLoaderPlugIn">
          <set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/>
          </plug-in>


          </struts-config>


          清單 5 是一個(gè)典型的 struts-config.xml 文件,只有一個(gè)小小的差別。它注冊(cè) Spring 代理類(lèi)的名稱(chēng),而不是聲明動(dòng)作的類(lèi)名,如(1)處所示。DelegatingActionProxy 類(lèi)使用動(dòng)作映射名稱(chēng)查找 Spring 環(huán)境中的動(dòng)作。這就是我們使用 ContextLoaderPlugIn 聲明的環(huán)境。

          將一個(gè) Struts 動(dòng)作注冊(cè)為一個(gè) Spring bean 是非常直觀(guān)的,如清單 6 所示。我利用動(dòng)作映射使用 <bean> 標(biāo)記的名稱(chēng)屬性(在這個(gè)例子中是 "/searchSubmit")簡(jiǎn)單地創(chuàng)建了一個(gè) bean。這個(gè)動(dòng)作的 JavaBean 屬性像任何 Spring bean 一樣被填充:


          清單 6. 在 Spring 環(huán)境中注冊(cè)一個(gè) Struts 動(dòng)作

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
          "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>
          <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>

          <bean name="/searchSubmit"
          class="ca.nexcel.books.actions.SearchSubmit">
          <property name="bookService">
          <ref bean="bookService"/>
          </property>
          </bean>

          </beans>






          動(dòng)作委托的優(yōu)點(diǎn)

          動(dòng)作委托解決方法是這三種方法中最好的。Struts 動(dòng)作不了解 Spring,不對(duì)代碼作任何改變就可用于非 Spring 應(yīng)用程序中。RequestProcessor 的改變不會(huì)影響它,并且它可以利用 Spring AOP 特性的優(yōu)點(diǎn)。

          動(dòng)作委托的優(yōu)點(diǎn)不止如此。一旦讓 Spring 控制您的 Struts 動(dòng)作,您就可以使用 Spring 給動(dòng)作補(bǔ)充更強(qiáng)的活力。例如,沒(méi)有 Spring 的話(huà),所有的 Struts 動(dòng)作都必須是線(xiàn)程安全的。如果您設(shè)置 <bean> 標(biāo)記的 singleton 屬性為“false”,那么不管用何種方法,您的應(yīng)用程序都將在每一個(gè)請(qǐng)求上有一個(gè)新生成的動(dòng)作對(duì)象。您可能不需要這種特性,但是把它放在您的工具箱中也 很好。您也可以利用 Spring 的生命周期方法。例如,當(dāng)實(shí)例化 Struts 動(dòng)作時(shí),<bean> 標(biāo)記的 init-method 屬性被用于運(yùn)行一個(gè)方法。類(lèi)似地,在從容器中刪除 bean 之前,destroy-method 屬性執(zhí)行一個(gè)方法。這些方法是管理昂貴對(duì)象的好辦法,它們以一種與 Servlet 生命周期相同的方式進(jìn)行管理。





          攔截 Struts

          前 面提到過(guò),通過(guò)將 Struts 動(dòng)作委托給 Spring 框架而整合 Struts 和 Spring 的一個(gè)主要的優(yōu)點(diǎn)是:您可以將 Spring 的 AOP 攔截器應(yīng)用于您的 Struts 動(dòng)作。通過(guò)將 Spring 攔截器應(yīng)用于 Struts 動(dòng)作,您可以用最小的代價(jià)處理橫切關(guān)注點(diǎn)。

          雖然 Spring 提供很多內(nèi)置攔截器,但是我將向您展示如何創(chuàng)建自己的攔截器并把它應(yīng)用于一個(gè) Struts 動(dòng)作。為了使用攔截器,您需要做三件事:

          1. 創(chuàng)建攔截器。
          2. 注冊(cè)攔截器。
          3. 聲明在何處攔截代碼。

          這看起來(lái)非常簡(jiǎn)單的幾句話(huà)卻非常強(qiáng)大。例如,在清單 7 中,我為 Struts 動(dòng)作創(chuàng)建了一個(gè)日志記錄攔截器。 這個(gè)攔截器在每個(gè)方法調(diào)用之前打印一句話(huà):


          清單 7. 一個(gè)簡(jiǎn)單的日志記錄攔截器

          package ca.nexcel.books.interceptors;

          import org.springframework.aop.MethodBeforeAdvice;

          import java.lang.reflect.Method;

          public class LoggingInterceptor implements MethodBeforeAdvice {

          public void before(Method method, Object[] objects, Object o) throws Throwable {
          System.out.println("logging before!");
          }
          }


          這個(gè)攔截器非常簡(jiǎn)單。before() 方法在攔截點(diǎn)中每個(gè)方法之前運(yùn)行。在本例中,它打印出一句話(huà),其實(shí)它可以做您想做的任何事。下一步就是在 Spring 配置文件中注冊(cè)這個(gè)攔截器,如清單 8 所示:


          清單 8. 在 Spring 配置文件中注冊(cè)攔截器

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
          "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>
          <bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/>

          <bean name="/searchSubmit"
          class="ca.nexcel.books.actions.SearchSubmit">
          <property name="bookService">
          <ref bean="bookService"/>
          </property>
          </bean>

          <!-- Interceptors -->
          <bean name="logger"
          class="ca.nexcel.books.interceptors.LoggingInterceptor"/> |(1)

          <!-- AutoProxies -->
          <bean name="loggingAutoProxy"
          class="org.springframework.aop.framework.autoproxy.
          BeanNameAutoProxyCreator"> |(2)
          <property name="beanNames">
          <value>/searchSubmit</valuesgt; |(3)
          </property>
          <property name="interceptorNames">
          <list>
          <value>logger</value> |(4)
          </list>
          </property>
          </bean>

          </beans>


          您可能已經(jīng)注意到了,清單 8 擴(kuò)展了 清單 6 中所示的應(yīng)用程序以包含一個(gè)攔截器。具體細(xì)節(jié)如下:

          • 在 (1) 處,我注冊(cè)了這個(gè)攔截器。
          • 在 (2) 處,我創(chuàng)建了一個(gè) bean 名稱(chēng)自動(dòng)代理,它描述如何應(yīng)用攔截器。還有其他的方法定義攔截點(diǎn),但是這種方法常見(jiàn)而簡(jiǎn)便。
          • 在 (3) 處,我將 Struts 動(dòng)作注冊(cè)為將被攔截的 bean。如果您想要攔截其他的 Struts 動(dòng)作,則只需要在 "beanNames" 下面創(chuàng)建附加的 <value> 標(biāo)記。
          • 在 (4) 處,當(dāng)攔截發(fā)生時(shí),我執(zhí)行了在 (1) 處創(chuàng)建的攔截器 bean 的名稱(chēng)。這里列出的所有攔截器都應(yīng)用于“beanNames”。

          就是這樣。就像這個(gè)例子所展示的,將您的 Struts 動(dòng)作置于 Spring 框架的控制之下,為處理您的 Struts 應(yīng)用程序提供了一系列全新的選擇。在本例中,使用動(dòng)作委托可以輕松地利用 Spring 攔截器提高 Struts 應(yīng)用程序中的日志記錄能力。





          結(jié)束語(yǔ)

          在本文中,您已經(jīng)學(xué)習(xí)了將 Struts 動(dòng)作整合到 Spring 框架中的三種竅門(mén)。使用 Spring 的 ActionSupport 來(lái)整合 Struts(第一種竅門(mén)中就是這樣做的)簡(jiǎn)單而快捷,但是會(huì)將 Struts 動(dòng)作與 Spring 框架耦合在一起。如果您需要將應(yīng)用程序移植到一個(gè)不同的框架,則需要重寫(xiě)代碼。第二種解決方法通過(guò)委托 RequestProcessor 巧妙地解開(kāi)代碼的耦合,但是它的可擴(kuò)展性不強(qiáng),并且當(dāng) Struts 的 RequestProcessor 變成一系列命令時(shí),這種方法就持續(xù)不了很長(zhǎng)時(shí)間。第三種方法是這三種方法中最好的:將 Struts 動(dòng)作委托給 Spring 框架可以使代碼解耦,從而使您可以在您的 Struts 應(yīng)用程序中利用 Spring 的特性(比如日志記錄攔截器)。

          三種 Struts-Spring 整合竅門(mén)中的每一種都被實(shí)現(xiàn)成一個(gè)完整可用的應(yīng)用程序。請(qǐng)參閱 下載 部分仔細(xì)研究它們。






          下載
          描述名字大小 下載方法
          ActionSupport sample codej-sr2-actionsupport.zip5 MB  FTP
          RequestProcessor sample codej-sr2-requestprocessor.zip5 MB  FTP
          Delegate sample codej-sr2-delegate.zip5 MB  FTP

          posted @ 2005-11-21 15:42 DenisLing 閱讀(1080) | 評(píng)論 (2)編輯 收藏

          僅列出標(biāo)題  
          主站蜘蛛池模板: 漳浦县| 怀安县| 宜宾县| 兖州市| 依兰县| 安宁市| 图们市| 台东县| 永泰县| 房山区| 商城县| 收藏| 上虞市| 托克逊县| 永康市| 闽侯县| 沂源县| 德州市| 安阳市| 玉环县| 邹平县| 崇义县| 扶余县| 安国市| 礼泉县| 万源市| 津市市| 大庆市| 新竹市| 治县。| 呼和浩特市| 行唐县| 株洲市| 文安县| 沂南县| 合水县| 宣武区| 怀宁县| 元谋县| 淳化县| 安泽县|