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