AOP學(xué)習(xí)(一)
說(shuō)明:本文大部分來(lái)自Spring in action。1、概述
SpringIoC容器能管理和配置應(yīng)用系統(tǒng)對(duì)象,因此我們可以遵行完美的面向隊(duì)象設(shè)計(jì),編寫松耦合的代碼,利用Spring的反向控制無(wú)痛連接協(xié)作類。但有時(shí)某些功能會(huì)在整個(gè)系統(tǒng)中到處用到,這就不是很適合對(duì)象繼承關(guān)系來(lái)解決。這就是面向切面(AOP)的切入點(diǎn)。
Spring的AOP框架將分散在系統(tǒng)中的功能塊放在一個(gè)地方——切面。依賴Spring的強(qiáng)大的切入點(diǎn)機(jī)制,何時(shí)何地在系統(tǒng)中采用切面有很多種選擇。
使用AOP,可以在一個(gè)地方定義通用功能,可以定義何時(shí)何地應(yīng)用這些功能,而不用在需要新功能的地方修改代碼。交叉業(yè)務(wù)(AOP實(shí)際上指的是交叉業(yè)務(wù)模塊化)可以模塊化到特定的對(duì)象切面中。這樣做有兩個(gè)好處。第一,現(xiàn)在每個(gè)業(yè)務(wù)邏輯放在一個(gè)地方,而不是分散到代碼的各個(gè)角落。第二,我們的服務(wù)模塊更加清晰,因?yàn)樗麄冎话麄兊暮诵墓δ埽o助功能轉(zhuǎn)移到切面中。
AOP從程序運(yùn)行的角度考慮程序的結(jié)構(gòu),提取業(yè)務(wù)處理過(guò)程的切面。
AOP面對(duì)程序運(yùn)行中各個(gè)步驟,以期待降低各步驟之間的耦合,從而提高步驟之間的隔離。
OOP是靜態(tài)的抽象,它對(duì)應(yīng)用中的實(shí)體及其屬性、行為進(jìn)行抽象,從而獲得清晰高效的單元?jiǎng)澐郑欢鳤OP是動(dòng)態(tài)的抽象,它對(duì)應(yīng)用執(zhí)行的過(guò)程的步驟進(jìn)行抽象,從而獲得步驟之間的邏輯劃分。AOP框架并不與特定的代碼耦合,能處理程序執(zhí)行中的特定的點(diǎn),而不是某個(gè)具體的程序。
2、術(shù)語(yǔ)
切面(Aspect)
切面是你要實(shí)現(xiàn)的交叉功能。它是應(yīng)用系統(tǒng)模塊化的一個(gè)切面或領(lǐng)域。切面的最常見(jiàn)(雖然簡(jiǎn)單)的例子是日志記錄。日志記錄在系統(tǒng)中到處用到,利用繼承來(lái)重用日志模塊不合適。然而,你可以創(chuàng)建一個(gè)日志記錄切面,并且使用AOP在系統(tǒng)中應(yīng)用。
連接點(diǎn)(Joinpoint)
連接點(diǎn)是應(yīng)用程序執(zhí)行過(guò)程中插入的切面的地點(diǎn)。這個(gè)地點(diǎn)可以是方法調(diào)用,異常拋出,或者甚至是要修改的字段。切面代碼在這些地方插入到你的應(yīng)用系統(tǒng)中,添加新的行為。
通知(Advice)
切面的實(shí)際實(shí)現(xiàn)。它通知應(yīng)用系統(tǒng)新的行為。在日志的例子中,日志通知包含了實(shí)現(xiàn)實(shí)際日志功能的代碼,如向日志文件寫日志。通知在連接點(diǎn)插入到應(yīng)用系統(tǒng)中。
切入點(diǎn)(Pointcut)
切入點(diǎn)定義了通知應(yīng)該應(yīng)用在哪些連接點(diǎn)。通知可以應(yīng)用到AOP框架支持的任何連接點(diǎn)。當(dāng)然,你并不希望把所有的切面應(yīng)用到所有可能的連接點(diǎn)上。切入點(diǎn)讓你指定通知應(yīng)用到什么地方。通常通過(guò)指定類名和方法名,或者匹配類名和方法名式樣的正則表達(dá)式來(lái)指定切入點(diǎn)。一些AOP框架允許動(dòng)態(tài)創(chuàng)建切入點(diǎn),在運(yùn)行時(shí)根據(jù)條件決定是否應(yīng)用切面,如方法參數(shù)等。
引入(Introduction)
引入允許你為已存在類添加新方法和屬性(哈,是種幻覺(jué)?)。例如,你可以創(chuàng)建一個(gè)稽查通知來(lái)記錄對(duì)象的最后修改時(shí)間。只要用一個(gè)方法setLastModified(Date)以及一個(gè)保存這個(gè)狀態(tài)的變量。可以在不改變已存在類的情況下將這個(gè)引入,給他們新的行為和狀態(tài)。
目標(biāo)對(duì)象(Target)
目標(biāo)對(duì)象是被通知對(duì)象,它可以是你編寫的類,也可以是你定制行為的第三方類。如果沒(méi)有AOP,這個(gè)類就必須要包含它的主要邏輯以及交叉業(yè)務(wù)邏輯。有了AOP,目標(biāo)對(duì)象就可以全身心地關(guān)注主要業(yè)務(wù),忘記應(yīng)用其上的通知。
代理(Proxy)
代理是將通知應(yīng)用到目標(biāo)對(duì)象后創(chuàng)建的對(duì)象。對(duì)于客戶對(duì)象來(lái)說(shuō),目標(biāo)對(duì)象(應(yīng)用AOP之前的對(duì)象)和代理對(duì)象(應(yīng)用AOP之后的對(duì)象)是一樣的。也就是說(shuō),應(yīng)用系統(tǒng)的其他部分不用為了支持代理對(duì)象而改變。
織入(Weaving)
織入是將切面應(yīng)用到目標(biāo)對(duì)象從而創(chuàng)建一個(gè)新的代理對(duì)象的過(guò)程。切面在指定接入點(diǎn)被織入到目標(biāo)對(duì)象中,織入發(fā)生在目標(biāo)對(duì)象生命周期的多個(gè)點(diǎn)上:
編譯器——切面在目標(biāo)對(duì)象編譯時(shí)織入。這需要一個(gè)特殊的編譯器
類裝載期——切面在目標(biāo)對(duì)象被載入到JVM中時(shí)織入。這需要一個(gè)特殊的類裝入器,它在類載入到應(yīng)用系統(tǒng)之前增強(qiáng)目標(biāo)對(duì)象的字解碼。
運(yùn)行期——切面在應(yīng)用系統(tǒng)運(yùn)行時(shí)織入。通常,AOP容器將織入切面的時(shí)候動(dòng)態(tài)生成委托目標(biāo)對(duì)象的代理對(duì)象。
3、Spring的AOP實(shí)現(xiàn)
不是所有的AOP框架都按同樣的方式實(shí)現(xiàn)。他們?cè)谀軌蛱峁┑慕尤朦c(diǎn)種類上有所不同。有些允許你在字段修改級(jí)別上應(yīng)用通知,而有些只能在方法調(diào)用上暴露連接點(diǎn)。他們?cè)诤螘r(shí)如何織入切面上也有所不同。無(wú)論什么情況,能夠創(chuàng)建連接點(diǎn),定義切面在哪些連接點(diǎn)織入是AOP框架的關(guān)鍵。
用Java編寫Spring的通知
在Spring中所有的通知都以Java類的形式編寫。這意味著你可以像普通Java開發(fā)那樣在集成環(huán)境中開發(fā)切面。定義在什么地方應(yīng)用通用的切入點(diǎn)通常編寫在Spring的配置文件中。
Spring的運(yùn)行時(shí)通知對(duì)象
代理Bean只有在第一次被應(yīng)用系統(tǒng)需要的時(shí)候才被創(chuàng)建。如果你使用的是ApplicationContext,代理對(duì)象在BeanFactory載入所有Bean的時(shí)候被創(chuàng)建。因?yàn)镾pring在運(yùn)行期創(chuàng)建代理,所有使用Spring AOP不需要特殊編譯器 。
Spring有兩種代理創(chuàng)建方式。如果目標(biāo)對(duì)象實(shí)現(xiàn)了一個(gè)(或多個(gè))接口暴露的方法,Spring將使用JDK的java.lang.reflect.Proxy類創(chuàng)建代理。這個(gè)類讓Spring動(dòng)態(tài)產(chǎn)生一個(gè)新的類,它實(shí)現(xiàn)了所需的接口,織入了通知,并且代理對(duì)目標(biāo)對(duì)象的所有請(qǐng)求。
如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)任何接口,Spring使用CGLIB庫(kù)庫(kù)生成目標(biāo)對(duì)象的子類。在創(chuàng)建這個(gè)子類的時(shí)候,Spring將通知織入,并且對(duì)目標(biāo)對(duì)象的調(diào)用委托給這個(gè)子類。當(dāng)使用這種代理生成方式時(shí),需要將Spring發(fā)型包中的lib/cglib目錄下的JAR文件發(fā)布到應(yīng)用系統(tǒng)中。在使用這種代理生成方式時(shí),需要注意兩個(gè)要點(diǎn):
對(duì)接口創(chuàng)建代理優(yōu)于對(duì)類創(chuàng)建代理,因?yàn)檫@樣會(huì)產(chǎn)生更加松耦合的系統(tǒng)。對(duì)類創(chuàng)建代理是讓遺留系統(tǒng)或無(wú)法實(shí)現(xiàn)接口的第三方類庫(kù)同樣可以得到通知。這種方式應(yīng)該是備用方式,而不是第一選擇。
標(biāo)記為final的方法不能被通知。記住,Spring是為目標(biāo)類產(chǎn)生子類。任何需要被通知的方法都被復(fù)寫,將通知織入。final方法是不可能做到的。
Spring只支持方法連接點(diǎn)
這一點(diǎn)和一些其他AOP框架不一樣,如AspectJ和JBoss,他們還提供了屬性接入點(diǎn),這樣可以防止你常見(jiàn)特別細(xì)致的通知,如對(duì)更新對(duì)象屬性值進(jìn)行攔截。
然而,由于Spring關(guān)注于提供一個(gè)實(shí)現(xiàn)J2EE服務(wù)的框架,所以方法攔截可以滿足大部分需求。加上Spring的觀點(diǎn)是屬性攔截破壞了封裝。面向?qū)ο蟮幕靖拍钍菍?duì)象自己處理工作,其他對(duì)象只能通過(guò)方法調(diào)用得到處理結(jié)果。讓通知觸發(fā)在屬性值改變而不是方法調(diào)用上無(wú)疑是破壞了這個(gè)概念。
posted on 2008-03-25 11:21 肖麥 閱讀(439) 評(píng)論(0) 編輯 收藏 所屬分類: Spring