這是本系列的最后一章,大象對(duì)示例進(jìn)行適當(dāng)?shù)臄U(kuò)充并說明。
其實(shí)到第四篇,對(duì)于示例的說明就已經(jīng)全部講完了,如果按照這樣的例子,很難有什么值得學(xué)習(xí)的地方。大象本著寫點(diǎn)有用東西的原則,在這章,對(duì)示例進(jìn)行一下適當(dāng)?shù)臄U(kuò)充并說明。
第五部分:擴(kuò)展框架
paramsPrepareParamsStack攔截器棧
paramsPrepareParamsStack這個(gè)攔截器棧是在struts2-default.xml中定義的,里面包含了很多個(gè)攔截器,最重要的是這三個(gè):params、prepare、modelDriven。我們只要記住這樣幾點(diǎn)。
params:它負(fù)責(zé)將請(qǐng)求參數(shù)值設(shè)置到Action中與之同名的屬性中。
prepare:當(dāng)Action實(shí)現(xiàn)了Preparable接口時(shí),這個(gè)攔截器就會(huì)調(diào)用prepare()方法。如果你有想在execute()方法之前執(zhí)行的邏輯處理,它就可以幫你完成這個(gè)功能。
modelDriven:如果Action實(shí)現(xiàn)了ModelDriven接口,這個(gè)攔截器就會(huì)把getModel()方法中的返回結(jié)果壓入值棧。這就意味著,可以在結(jié)果頁(yè)面上直接使用model對(duì)象的屬性。
它的執(zhí)行順序是這樣的
首先,params攔截器會(huì)給action中的相關(guān)參數(shù)賦值,如id,username,password等等。
然后,prepare攔截器執(zhí)行prepare()方法,prepare()會(huì)根據(jù)參數(shù),如id,去調(diào)用相關(guān)的方法,設(shè)置model對(duì)象。當(dāng)然,實(shí)現(xiàn)的這個(gè)接口方法由你自己來定義,不局限只設(shè)置model之類的功能。
接著,modelDriven攔截器會(huì)將model對(duì)象壓入值棧,因?yàn)樗前?/span>getModel()方法中的返回結(jié)果放到值棧中,而這個(gè)方法的返回類型是個(gè)泛型參數(shù),在實(shí)現(xiàn)ModelDriven接口的時(shí)候,可以給它指定一個(gè)具體的對(duì)象類型,因此返回類型也將是這個(gè)指定的對(duì)象類型,如ModelDriven<User>
最后,params攔截器會(huì)將參數(shù)再賦值給model對(duì)象。
思考修改與保存這兩種動(dòng)作。當(dāng)點(diǎn)擊人員修改時(shí),請(qǐng)求為:user!input.action?id=1,params攔截器會(huì)將id參數(shù)值設(shè)置到Action中的id屬性,請(qǐng)一定注意,id屬性要有set()方法,然后prepare攔截器開始在prepare()方法中,根據(jù)這個(gè)id值取得User對(duì)象,接著modelDriven會(huì)調(diào)用getModel()方法,此時(shí),方法中返回的是user對(duì)象,所以會(huì)把user加入到值棧中,最后再執(zhí)行一次params攔截器,但這時(shí)沒有其它的參數(shù)值需要賦值給user對(duì)象,所以程序會(huì)接著往下走,這里假定沒有其它的業(yè)務(wù)邏輯,執(zhí)行返回,字符串為input,根據(jù)前面講的插件知識(shí),結(jié)果頁(yè)面為user-input.jsp,那么就跳轉(zhuǎn)到修改頁(yè)面了,而且頁(yè)面中表單域?qū)@示數(shù)據(jù)庫(kù)中的值。如果理解了修改,那么保存也就清楚了。
prepare()方法雖然不錯(cuò),但是也有弊端,那就是它會(huì)對(duì)Action中的每個(gè)方法都進(jìn)行攔截,不管你是執(zhí)行execute還是input,還是其它的自定義方法,它都會(huì)對(duì)其攔截,這當(dāng)然不是我們所希望的。那有沒有更好的方式?答案是肯定的,請(qǐng)接著往下看。
prepareMethedName
使用prepare攔截器的另一種形式,在prepare名稱后面加上需要攔截的方法名。比如,你要攔截input方法,可以寫成prepareInput,需要攔截save方法,就寫上prepareSave。采取這樣的方式后,將會(huì)在執(zhí)行這些方法之前時(shí),才對(duì)它們進(jìn)行攔截。
例如,請(qǐng)求role!input.action,會(huì)執(zhí)行RoleAction中的input方法,如果我們?cè)O(shè)置了prepareInput方法,則會(huì)先進(jìn)入此方法執(zhí)行,執(zhí)行完后再回到input方法往下執(zhí)行。
請(qǐng)注意,在使用這種方式時(shí),Preparable接口定義的prepare()方法體內(nèi)不要含有任何代碼,就是說給這個(gè)方法一個(gè)空實(shí)現(xiàn)。這樣,它就什么都不做,所有的攔截處理就全部交由相應(yīng)的prepareMethedName來完成。


它們?cè)诿總€(gè)對(duì)應(yīng)的方法之前執(zhí)行。prepareEntity就是來初始化實(shí)體對(duì)象,然后由modelDriven攔截器將getModel()方法中的返回結(jié)果放入值棧,當(dāng)返回頁(yè)面時(shí),就可以直接取值了。
StrutsAction
重新定義一個(gè)基類,里面封裝大部分的通用操作,主要依靠泛型來實(shí)現(xiàn),將hibernateDao注解進(jìn)來,通過繼承這個(gè)基類進(jìn)行基本的CRUD操作。本文末尾提供示例源碼下載,里面有詳細(xì)的注釋,這里我只貼出部分重要代碼進(jìn)行說明,為了行文需要,有些注釋去掉了,但源碼里面都有,請(qǐng)大家放心。

通過擴(kuò)展ActionSupport,使用泛型參數(shù),構(gòu)造函數(shù)根據(jù)反射得到T的具體類型。

這就是默認(rèn)的執(zhí)行方法,基本的操作,在這個(gè)超類里面都進(jìn)行了定義,每個(gè)方法里面設(shè)置的以do開頭的方法,是方便讓子類進(jìn)行覆蓋,當(dāng)基本的業(yè)務(wù)邏輯無法滿足我們的需求時(shí),就可以在子類重寫這些方法。

方法有默認(rèn)實(shí)現(xiàn),主要是列表顯示,保存和刪除,新增和修改已經(jīng)有getModel()方法取得實(shí)體,在頁(yè)面上使用s標(biāo)簽就可以直接取值,除非有特殊的業(yè)務(wù)需求,否則不用覆蓋doInputEntity()、doViewEntity()方法。請(qǐng)注意,當(dāng)需要實(shí)現(xiàn)自己的邏輯時(shí),只需要覆蓋上面定義的這些方法,而不用重寫execute、input之類。
我對(duì)HibernateDao又進(jìn)行了適當(dāng)?shù)臄U(kuò)展與修改,提供了更多的基本封裝方法,不過大家還可以繼續(xù)添加。里面都有詳細(xì)的注釋,這里就不在贅述了。
功能擴(kuò)展
我對(duì)例子做了兩個(gè)功能,一個(gè)是角色表的增加、修改、刪除、查看,另一個(gè)就是用戶表的查詢。可以從源碼中看到,我在RoleAction中沒有寫一行關(guān)于增刪改查的代碼,因?yàn)樗鼘儆诨静僮鳎愔幸呀?jīng)封裝好了,所以這部分的代碼都省了。對(duì)于用戶表的查詢,我覆蓋了doListEntity()方法,在業(yè)務(wù)層進(jìn)行條件封裝,執(zhí)行查詢,返回結(jié)果。

這個(gè)list就是在超類中定義的,因?yàn)槟J(rèn)實(shí)現(xiàn)中也用到了list,另外list有一個(gè)get方法,用于在頁(yè)面中顯示。如果不想采取方式取得list集合,就重寫doListEntity()方法。這里說明下,我是沒有加分頁(yè)功能的,大家可以按自己的方式添加分頁(yè)查詢。
在用戶查詢方法中,我使用的是QBC對(duì)象查詢,因?yàn)檫@種方式很簡(jiǎn)潔,不過我在HibernateDao中也寫了HQL和SQL方式的查詢方法,并進(jìn)行了封裝,可以很方便的調(diào)用。


這個(gè)queryResult方法的定義,你可以改為傳遞用戶名與角色ID的參數(shù),大象在這里就是為了方便,直接使用Request請(qǐng)求。這里userDao調(diào)用的query方法是在HibernateDao里面封裝的,因?yàn)槔^承了HibernateDao,就直接在Service層拿來用了。至于具體的,可以去看源碼。
頁(yè)面部分沒有進(jìn)行大的調(diào)整,主要是將role-list.jsp重命名為role.jsp,因?yàn)槭褂玫氖浅惖哪J(rèn)實(shí)現(xiàn)。添加了role-input.jsp和role-view.jsp兩個(gè)文件,并在user.jsp中,加入了查詢條件。這些代碼都很簡(jiǎn)單就不再貼了,而且前一篇也貼過一部分。
對(duì)于這個(gè)例子的完整講解說明就到此結(jié)束了。大象還想補(bǔ)充說明一下,這個(gè)例子只適用于學(xué)習(xí),不適合商用,想在實(shí)際項(xiàng)目中運(yùn)用,還需要對(duì)框架做大量的改造工作。本系列只是基于SSH2入門學(xué)習(xí)之用,源碼中不含jar包,下圖是本例中所需的最少jar文件,大家只要下載了spring、struts、hibernate三個(gè)完整壓縮包,那么這些jar基本上都包含了。

發(fā)布并啟動(dòng)Tomcat,然后輸入訪問地址:http://localhost:8080/ssh2 運(yùn)行該示例,看看效果。