卞世暉,李龍澍,陳圣兵,李 浩
(1.安徽大學(xué) 計(jì)算機(jī)學(xué)院,安徽 合肥 230039;2.武漢理工大學(xué) 計(jì)算機(jī)學(xué)院,湖北 武漢430070)
面向?qū)ο缶幊蘋(píng)OP(Object-Oriented Programming)的開(kāi)發(fā)方法是把軟件系統(tǒng)看成各種對(duì)象的集合,采用抽象、封裝、繼承和多態(tài)的方法維護(hù)系統(tǒng)功能。但隨著OOP技術(shù)應(yīng)用實(shí)踐的增多和應(yīng)用范圍的擴(kuò)大,逐漸暴露出其不足和局限性。比如一個(gè)系統(tǒng)中有幾十個(gè)或幾百個(gè)數(shù)據(jù)庫(kù)查詢(xún)函數(shù),每個(gè)地方都要求記錄數(shù)據(jù)庫(kù)查詢(xún)的語(yǔ)句,OOP技術(shù)只能為每個(gè)函數(shù)增加記錄日志,卻沒(méi)有更好的辦法。而面向方面編程AOP(Aspect-Oriented Programming)技術(shù)則通過(guò)方法攔截解決,在每個(gè)方法調(diào)用前后或出現(xiàn)異常時(shí)記錄日志信息[1]。
AOP是通過(guò)分離核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn),削弱二者之間的耦合度。利用AOP技術(shù)可對(duì)相關(guān)的橫切關(guān)注點(diǎn)進(jìn)行封裝,從而保證橫切關(guān)注點(diǎn)的復(fù)用,并根據(jù)需要適時(shí)增添、撤除、改變橫切性關(guān)注點(diǎn),而不影響核心關(guān)注點(diǎn),提高系統(tǒng)的擴(kuò)展性和靈活性。文中Struts2中的攔截器采用AOP的設(shè)計(jì)理念,通過(guò)定義、配置攔截器,攔截器可以按照“插拔”方式攔截相應(yīng)的請(qǐng)求,配合業(yè)務(wù)控制器處理該請(qǐng)求。
攔截器采用AOP的設(shè)計(jì)理念,攔截是AOP的一種實(shí)現(xiàn)策略。AOP是針對(duì)OOP的局限性所提出的一種新的編程思想,是對(duì)OOP的補(bǔ)充和完善,它可以有效提高代碼復(fù)用性,提高開(kāi)發(fā)效率[2]。OOP技術(shù)引入封裝、繼承和多態(tài)性等概念建立一種對(duì)象層次結(jié)構(gòu),用以模擬公共行為的一個(gè)集合。但OOP不能為分散的對(duì)象引入公共行為。也就是OOP允許定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如安全驗(yàn)證、日志等功能往往水平散布在所有對(duì)象層次中,而與其所散布到的對(duì)象核心功能沒(méi)有關(guān)系。這些散布在各處的代碼被稱(chēng)為橫切代碼,在OOP設(shè)計(jì)中,它們?cè)斐纱罅看a的重復(fù),不利于各個(gè)模塊的復(fù)用。而AOP則可以減少系統(tǒng)中的重復(fù)代碼,有效降低各模塊間的耦合度,方便以后的維護(hù)和操作,它采用一種稱(chēng)為“橫切”的技術(shù),剖解開(kāi)封裝的對(duì)象內(nèi)部,將影響多個(gè)類(lèi)的公共行為封裝到一個(gè)可重用模塊,并將其稱(chēng)為“方面”。AOP代表一種橫向的關(guān)系,在A(yíng)OP中,將具有公共邏輯的、與其他模塊的核心模塊糾纏在一起的行為稱(chēng)為“橫切關(guān)注點(diǎn)”,AOP就是要封裝這些橫切性關(guān)注點(diǎn),以實(shí)現(xiàn)代碼的復(fù)用性、靈活性和擴(kuò)展性。
攔截器是可在某個(gè)方法或字段訪(fǎng)問(wèn)之前進(jìn)行攔截,然后在之前或之后加入某些操作的一種程序設(shè)計(jì)思想[3]。AOP通過(guò)攔截實(shí)現(xiàn)關(guān)注點(diǎn)織入,一般要攔截一個(gè)方法可采用回調(diào)方法或動(dòng)態(tài)代理。由于動(dòng)態(tài)代理比較靈活,所以大多數(shù)AOP都采用動(dòng)態(tài)代理實(shí)現(xiàn),代理對(duì)象提供一種代理方式控制原對(duì)象的訪(fǎng)問(wèn)[4]。JDK開(kāi)發(fā)包提供動(dòng)態(tài)代理支持,開(kāi)發(fā)者可通過(guò)實(shí)現(xiàn)java.lang.reflect.InvocationHandler接口提供一個(gè)執(zhí)行代理器,然后通過(guò)java.lang.reflect.Proxy得到一個(gè)代理對(duì)象,通過(guò)該代理對(duì)象執(zhí)行被代理類(lèi)(業(yè)務(wù)邏輯)方法,在被代理類(lèi)(業(yè)務(wù)邏輯)方法被調(diào)用的同時(shí),執(zhí)行處理器會(huì)被自動(dòng)調(diào)用。
Struts2中的攔截器如圖1所示。Struts2的Action被一個(gè)或多個(gè)攔截器所包圍,所有用戶(hù)請(qǐng)求都會(huì)被攔截器所攔截,然后交給Action處理。該調(diào)用流程由配置文件實(shí)現(xiàn),當(dāng)用戶(hù)請(qǐng)求到達(dá)Struts2的 ServletDispacher時(shí) ,Struts2會(huì)查找配置文件,并根據(jù)其配置實(shí)例化相對(duì)應(yīng)的攔截器對(duì)象,然后串成一個(gè)列表(list),最后逐個(gè)調(diào)用列表中的攔截器。
圖1 Struts2攔截器
通過(guò)攔截形成攔截器模塊,大大提高系統(tǒng)開(kāi)發(fā)的靈活性和復(fù)用性,AOP使用代理方式,將多個(gè)攔截器和核心業(yè)務(wù)邏輯組合在一起,滿(mǎn)足用戶(hù)業(yè)務(wù)需求,這種機(jī)制克服了傳統(tǒng)OOP設(shè)計(jì)思想所帶來(lái)的各種弊端,結(jié)合OOP設(shè)計(jì),為項(xiàng)目開(kāi)發(fā)提供較完善的解決思路。
很多公共操作并不是所有Action都要實(shí)現(xiàn)的,而攔截器能將這些公共操作進(jìn)行動(dòng)態(tài)組合,以“插拔”方式應(yīng)用到Action中[5]。開(kāi)發(fā)者可根據(jù)業(yè)務(wù)需要,分離出日志記錄、安全認(rèn)證、文件上傳、國(guó)際化等多個(gè)攔截器。攔截器的特點(diǎn)就是可插拔性[6],如圖 2所示,攔截器1和攔截器2都是可“插拔”的,用戶(hù)可以根據(jù)自己需要,增加或減少攔截器,或調(diào)整攔截器的順序。當(dāng)需要某個(gè)攔截器時(shí),只需“插入,不需要時(shí)“拔出”即可。
圖2 攔截器的插拔性
攔截器的“插拔“實(shí)際是通過(guò)Struts2框架的配置文件實(shí)現(xiàn)的,配置文件中可定義攔截器和攔截器棧,并可在A(yíng)ction中定義所使用的攔截器或攔截器棧(攔截器棧就是由多個(gè)攔截器組成的一個(gè)攔截器組)。在struts.xml配置文件中,攔截器的定義使用
定義好攔截器或攔截器棧后,可在配置文件中使用該攔截器或攔截器棧來(lái)攔截Action,指定的攔截器或攔截器棧會(huì)在A(yíng)ction的execute()方法執(zhí)行前被執(zhí)行。在配置文件的Action配置中,使用
攔截器可應(yīng)用于用戶(hù)的權(quán)限認(rèn)證、系統(tǒng)的日志記錄等方面,這里以一個(gè)銀行業(yè)務(wù)的日志記錄為例說(shuō)明攔截器的應(yīng)用。銀行的每一個(gè)業(yè)務(wù)操作(如轉(zhuǎn)賬、存款、匯款等),基本都要進(jìn)行日志記錄。在傳統(tǒng)設(shè)計(jì)中,要在每一個(gè)業(yè)務(wù)操作前后進(jìn)行相同記錄(如經(jīng)辦人、交易時(shí)間、交易金額一類(lèi)),不僅造成代碼的臃腫,而且使得這些日志記錄與業(yè)務(wù)操作耦合在一起,不利于系統(tǒng)的維護(hù)和升級(jí),靈活性也較差。而采用AOP的設(shè)計(jì)思想,則可把業(yè)務(wù)操作的日志記錄作為一個(gè)“橫切關(guān)注點(diǎn)”分離出來(lái),分離出來(lái)的日志記錄模塊在Struts2中應(yīng)用攔截器實(shí)現(xiàn),并不會(huì)影響業(yè)務(wù)模塊的實(shí)現(xiàn)。需要時(shí)只要把該攔截器“插入”到Aicton中即可。首先建立一個(gè)日志記錄攔截器,其關(guān)鍵代碼如下:
定義好攔截器后,就可以在配置文件中使用。為了使用日志記錄攔截器,需要建立一個(gè)Action。由于設(shè)置了攔截器,所以可在A(yíng)ction前后自動(dòng)執(zhí)行,很方便地在需要時(shí)加入該攔截器,而且這種方式是動(dòng)態(tài)的,并且功能自由組合。
攔截器采用一種橫切式、可插拔的設(shè)計(jì)思想,提高了代碼的靈活性。攔截器更為靈活的使用方法,如攔截器的方法過(guò)濾、攔截器的執(zhí)行順序、設(shè)置攔截器參數(shù)等,進(jìn)一步增強(qiáng)了攔截器的靈活性。攔截器是實(shí)現(xiàn)代碼復(fù)用性、擴(kuò)展性的有效方法,因此會(huì)得到更廣泛應(yīng)用。
[1]Walls C,Breidenbach R.Spring in action[M].Beijing:Manning Publications,2007.
[2]張英捷,劉萬(wàn)軍.Spring AOP技術(shù)在J2EE系統(tǒng)安全性驗(yàn)證中的應(yīng)用研究[J].計(jì)算機(jī)科學(xué)與工程,2008,30(8):137-138.
[3]王濤濤,李曉禹,施煒利.Struts2攔截器控制頁(yè)面訪(fǎng)問(wèn)權(quán)限的設(shè)計(jì)與實(shí)現(xiàn)[J].計(jì)算機(jī)與現(xiàn)代化,2009(1):32-33.
[4]曹曉利,郭順生.AOP技術(shù)及其在J2EE中的動(dòng)態(tài)代理實(shí)現(xiàn)[J].計(jì)算機(jī)技術(shù)與發(fā)展,2008,18(11):121-122.
[5]崔群法,王詠梅,李有軍.Struts2.0從入門(mén)到精通[M].北京:電子工業(yè)出版社,2009.
[6]閆術(shù)卓,楊 強(qiáng).Struts2技術(shù)詳解[M].北京:電子工業(yè)出版社,2008.