定義
官方解釋
AOP,Aspect Oriented Programming的縮寫,意為面向切面編程朋贬,通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)凯楔。
AOP通俗理解:
一個(gè)組件A,不關(guān)心其它常用的服務(wù)組件B锦募,但是A要使用組件B的時(shí)候摆屯,不是組件A自己去調(diào)用,而是通過配置等其他方式糠亩,比如Spring中的xml配置文件虐骑,這樣A不需要知道組件B的任何事情,A只關(guān)心自己的業(yè)務(wù)邏輯赎线,具體在A使用B的過程中廷没,配置文件去做,與具體的A組件無關(guān)垂寥。
例子
要理解AOP(面向切面編程)颠黎,首先我們要明白什么叫做切面。
??舉個(gè)生活中的例子滞项,我們都知道一個(gè)成功的老板背后一定有一個(gè)優(yōu)秀的秘書盏缤,而秘書的職責(zé)就是負(fù)責(zé)調(diào)度和安排老板的日程。想象一下這樣一個(gè)場(chǎng)景蓖扑,今天秘書小M將事先安排好的一天日程向老板大B匯報(bào)后,老板大B表示同意台舱。那么如無意外律杠,老板大B今天將和往常一樣,按照日程表一樣一樣完成竞惋,一天就過去了柜去。但是,秘書小M突然接到通知拆宛,一個(gè)十分重要的會(huì)議臨時(shí)決定在午后召開嗓奢,無奈秘書小M只能將該會(huì)議強(qiáng)行插入老板大B已經(jīng)確認(rèn)過的日程安排中,然后默默等待老板大B的白眼浑厚。
我們注意到秘書小M將臨時(shí)會(huì)議插入日程的動(dòng)作股耽,是不是很符合切面這個(gè)感覺呢?更形象一點(diǎn)钳幅,我們將老板大B確認(rèn)過的日程想象成一條長(zhǎng)面包物蝙,而秘書小M突然被告知還有一片火腿(即那個(gè)討厭的臨時(shí)會(huì)議),為了老板能夠吃好敢艰,不得已只能用小刀將面包切開后硬生生將火腿塞入诬乞。
??可以這樣理解,面向切面就是為了達(dá)到目的,動(dòng)態(tài)地將某件事切入到某件事中震嫉。
程序中的AOP
更容易理解的定義:運(yùn)行時(shí)森瘪,動(dòng)態(tài)地將代碼切入到類的指定方法或者指定位置
此處介紹幾個(gè)概念:
??通知(advice):切入到類指定方法或者指定位置的代碼片段,即需要增加的功能代碼票堵,也就是上述的那片火腿(臨時(shí)會(huì)議)扼睬。
??連接點(diǎn)(join point):程序運(yùn)行過程中能夠進(jìn)行插入切面操作的時(shí)間點(diǎn)。例如方法調(diào)用换衬、異常拋出或字段修改等痰驱,可以理解為上述的長(zhǎng)面包(老板的整個(gè)日程安排)。
??切入點(diǎn)(pointcut):描述一個(gè)通知將被切入的一系列連接點(diǎn)的集合瞳浦,即代碼片段具體切入到哪些類担映、哪些方法,也就是上述面包切口處(特指午后的日程)叫潦。所以說蝇完,切入點(diǎn)規(guī)定了哪些連接點(diǎn)可以執(zhí)行哪些通知。
??切面(aspect):AOP中的切面等同于OOP中的類(class)矗蕊,由通知(advice)和切入點(diǎn)(pointcut)組成短蜕,其中通知(advice)和切入點(diǎn)(pointcut)既可以是1對(duì)1的關(guān)系,也可以是1對(duì)多的關(guān)系傻咖。概括的說就是描述了何時(shí)何地干何事的基本單元朋魔,其中通知(advice)說明了切面干何事,而切入點(diǎn)則說明了切面何時(shí)何地切入卿操。
織入(Weaving)警检,AOP術(shù)語。把切面(aspect)連接到其它的應(yīng)用程序類型或者對(duì)象上害淤,并創(chuàng)建一個(gè)被通知(advised)的對(duì)象扇雕,這樣的行為叫做織入。
關(guān)于幾者的關(guān)系窥摄,我們可以這樣理解镶奉,通知是在連接點(diǎn)上執(zhí)行的,但是我們不希望通知應(yīng)用到所有的連接點(diǎn)崭放,所以引入了切入點(diǎn)來匹配特定的連接點(diǎn)哨苛,指名我們所希望通知應(yīng)用的連接點(diǎn)。
??因此莹菱,所謂的AOP(面向切面編程)就是在程序運(yùn)行過程中的某個(gè)時(shí)機(jī)將代碼片段插入到某些類的指定方法和指定位置移国;換句話說,秘書接到臨時(shí)會(huì)議通知時(shí)道伟,將臨時(shí)會(huì)議插入到老板的日程安排中去迹缀。
為什么要使用AOP
程序的最終目的就是實(shí)現(xiàn)業(yè)務(wù)使碾,但是我們?cè)谶M(jìn)行編程的過程中經(jīng)常會(huì)發(fā)現(xiàn)除了所謂的業(yè)務(wù)代碼,還存在數(shù)量相當(dāng)?shù)墓泊a祝懂,類似日志票摇、安全驗(yàn)證、事物砚蓬、異常處理等問題矢门。這部分代碼重要但是與我們編寫程序要實(shí)現(xiàn)的功能沒有關(guān)系,具有功能相似灰蛙、重用性高祟剔、使用場(chǎng)景分散等特點(diǎn)。我們姑且稱它們?yōu)?strong>共性問題摩梧。
??對(duì)大多數(shù)程序而言物延,代碼都是以縱向結(jié)構(gòu)將各個(gè)業(yè)務(wù)模塊串聯(lián)從而完成功能的。我們提到的共性問題本身不屬于業(yè)務(wù)范圍仅父,但是又散落在各個(gè)業(yè)務(wù)模塊間叛薯,同實(shí)現(xiàn)主功能的代碼相互雜糅在一起,即如下圖所示:
試想一下笙纤,如果將共性問題部分的代碼融入業(yè)務(wù)代碼中耗溜,一旦涉及到對(duì)某個(gè)共性問題部分的代碼進(jìn)行更改的時(shí)候,例如日志部分發(fā)生需求變更省容,我們可能需要牽涉許許多多其他模塊代碼抖拴。這在小規(guī)模程序中也許是可以接受的,可能只修改1腥椒、2處城舞;但是如果牽涉的地方數(shù)量過多,特別是應(yīng)用在中大型規(guī)模程序中寞酿,我們甚至?xí)榱诵⌒〉囊粋€(gè)功能,修改上千脱柱、上萬處伐弹。這樣的方式是十分糟糕的,不僅費(fèi)時(shí)費(fèi)力榨为,可能還會(huì)引起一些不必要的麻煩(回歸錯(cuò)誤惨好、結(jié)構(gòu)混亂等等)。
??AOP的就是為了解決這類共性問題随闺,將散落在程序中的公共部分提取出來日川,以切面的形式切入業(yè)務(wù)邏輯中,使程序員只專注于業(yè)務(wù)的開發(fā)矩乐,從事務(wù)提交等與業(yè)務(wù)無關(guān)的問題中解脫出來龄句。
AOP的優(yōu)點(diǎn)
解耦:AOP將程序中的共性問題進(jìn)行了剝離回论,毫無疑問地降低了各個(gè)業(yè)務(wù)模塊和共性問題之間的耦合。
重用性:共性問題散落于業(yè)務(wù)邏輯的各處分歇,十分難維護(hù)傀蓉,使用AOP進(jìn)行提取后,能夠?qū)⑾嗨乒δ艿墓残詥栴}收斂职抡,減少重復(fù)代碼葬燎,提高了代碼的重用性。
拓展性:對(duì)于一個(gè)程序而言缚甩,迭代的重心一定在于業(yè)務(wù)和功能上谱净。AOP使得每當(dāng)發(fā)生變更時(shí),可以只關(guān)注業(yè)務(wù)邏輯相關(guān)的代碼擅威,而減少共性問題上帶來的變化壕探,大大降低了程序未來拓展的成本。
Spring AOP的實(shí)現(xiàn)
通過上述介紹裕寨,我們了解到AOP的重心在于定義基本結(jié)構(gòu)和完成切面切入兩部分浩蓉,解決了它們,我們就能方便快捷的使用AOP思想進(jìn)行編程了宾袜。
??Spring AOP就是負(fù)責(zé)完成AOP相關(guān)工作的框架捻艳,它將切面所定義的橫切邏輯切入到切面所指定的連接點(diǎn)中∏烀ǎ框架的目的就是將復(fù)雜的事情變得簡(jiǎn)單易用认轨,所以其主要工作分成了如下兩點(diǎn):
1.提供相應(yīng)的數(shù)據(jù)結(jié)構(gòu)來定義AOP所需基本結(jié)構(gòu),例如通知月培、連接點(diǎn)嘁字、切入點(diǎn)、切面等杉畜。
??2.封裝切面切入的相關(guān)工作纪蜒,提供相應(yīng)接口給用戶。封裝的內(nèi)容主要包括如何通過切面(切入點(diǎn)和通知)定位到特定的連接點(diǎn)此叠;如何將切面中的功能代碼植入到特定的連接點(diǎn)中等等纯续;提供相應(yīng)接口主要包括配置文件、注解等用戶能夠使用的工具灭袁。
??這里想提一下猬错,在 Spring AOP 中,連接點(diǎn)(join point)總是方法的執(zhí)行點(diǎn)茸歧, 即只有方法連接點(diǎn)倦炒。所以我們可以認(rèn)為,在 Spring 中所有的方法都可以是連接點(diǎn)软瞎。
Spring AOP的實(shí)現(xiàn)舉例
由于Spring對(duì)AOP的封裝逢唤,使得我們可以十分方便的使用拉讯,我們只需要定義切面,即定義Advice通知和Pointcut切入點(diǎn)智玻。
(1)Spring AOP中Pointcut切入點(diǎn)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
其中遂唧,除ret-type-pattern和name-pattern之外,其他都是可選的吊奢,各部分具體含義如下:
??1.modifiers-pattern:方法的操作權(quán)限
??2.ret-type-pattern:返回值
??3.declaring-type-pattern:方法所在的包
??4.name-pattern:方法名
??5.parm-pattern:參數(shù)名
??6.throws-pattern:異常
??為了更加理解盖彭,舉一個(gè)簡(jiǎn)單的execution示例,如下所示:
(2)Spring AOP中Advice通知
before advice:前置通知页滚,由@Before注解定義召边,表示在 join point 前被執(zhí)行的advice。(雖然before advice是在 join point 前被執(zhí)行裹驰,但是它并不能夠阻止 join point 的執(zhí)行隧熙, 除非發(fā)生了異常,即在before advice代碼中幻林, 不能人為地決定是否繼續(xù)執(zhí)行join point中的代碼)
after return advice:后置通知贞盯,由@AfterReturning注解定義,表示在一個(gè) join point 正常返回后執(zhí)行的advice沪饺。
after throwing advice:異常通知躏敢,由@AfterThrowing注解定義,表示當(dāng)一個(gè) join point 拋出異常后執(zhí)行的advice整葡。
after(final) advice:最終通知件余,由@After注解定義,表示無論一個(gè)join point是正常退出還是發(fā)生了異常遭居,都會(huì)被執(zhí)行的advice啼器。
around advice:環(huán)繞通知,由@Around注解定義俱萍,表示在join point 前和joint point退出后都執(zhí)行的 advice端壳,是最常用的advice。
AOP的實(shí)現(xiàn)
AOP的實(shí)現(xiàn)是基于代理機(jī)制的枪蘑,根據(jù)不同實(shí)現(xiàn)方式主要分為兩類:
??1.靜態(tài)代理更哄,AOP框架會(huì)在編譯階段生成AOP代理類,即在編譯器和類裝載期實(shí)現(xiàn)切入的工作腥寇,但是這種方式需要特殊的Java編譯器和類裝載器。AspectJ框架就是采用這種方式實(shí)現(xiàn)AOP觅捆。
??2.動(dòng)態(tài)代理赦役,AOP框架不會(huì)去修改字節(jié)碼,而是在內(nèi)存中臨時(shí)為方法生成一個(gè)AOP對(duì)象栅炒,這個(gè)AOP對(duì)象包含了目標(biāo)對(duì)象的全部方法掂摔,并且在特定的連接點(diǎn)(切入點(diǎn))做了添加通知(advice)處理术羔,并回調(diào)原對(duì)象的方法。
??與AspectJ的靜態(tài)代理不同乙漓,Spring AOP使用動(dòng)態(tài)代理级历,通過JDK Proxy和CGLIB Proxy兩種方法實(shí)現(xiàn)代理。兩種方式的選擇與目標(biāo)對(duì)象有關(guān):
- 如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)任何接口叭披,那么Spring將使用CGLIB來實(shí)現(xiàn)代理寥殖。CGLIB是一個(gè)開源項(xiàng)目,它是一個(gè)強(qiáng)大的涩蜘,高性能嚼贡,高質(zhì)量的Code生成類庫,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口同诫。
- 如果目標(biāo)對(duì)象實(shí)現(xiàn)了一個(gè)以上的接口粤策,那么Spring將使用JDK Proxy來實(shí)現(xiàn)代理,因?yàn)镾pring默認(rèn)使用的就是JDK Proxy误窖,并且JDK Proxy是基于接口的叮盘。這也是Spring提倡的面向接口編程。當(dāng)然霹俺,你也可以強(qiáng)制使用CGLIB來進(jìn)行代理柔吼,但是這樣可能會(huì)造成性能上的下降。