這種在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程。
面向切面編程(AOP是Aspect Oriented Program的首字母縮寫) ,我們知道,面向對象的特點是繼承、多態和封裝。而封裝就要求將功能分散到不同的對象中去,這在軟件設計中往往稱為職責分配。實際上也就是說,讓不同的類設計不同的方法。這樣代碼就分散到一個個的類中去了。這樣做的好處是降低了代碼的復雜程度,使類可重用。
但是人們也發現,在分散代碼的同時,也增加了代碼的重復性。什么意思呢?比如說,我們在兩個類中,可能都需要在每個方法中做日志。按面向對象的設計方法,我們就必須在兩個類的方法中都加入日志的內容。也許他們是完全相同的,但就是因為面向對象的設計讓類與類之間無法聯系,而不能將這些重復的代碼統一起來。
也許有人會說,那好辦啊,我們可以將這段代碼寫在一個獨立的類獨立的方法里,然后再在這兩個類中調用。但是,這樣一來,這兩個類跟我們上面提到的獨立的類就有耦合了,它的改變會影響這兩個類。那么,有沒有什么辦法,能讓我們在需要的時候,隨意地加入代碼呢?這種在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程。
一般而言,我們管切入到指定類指定方法的代碼片段稱為切面,而切入到哪些類、哪些方法則叫切入點。有了AOP,我們就可以把幾個類共有的代碼,抽取到一個切片中,等到需要時再切入對象中去,從而改變其原有的行為。
這樣看來,AOP其實只是OOP的補充而已。OOP從橫向上區分出一個個的類來,而AOP則從縱向上向對象中加入特定的代碼。有了AOP,OOP變得立體了。如果加上時間維度,AOP使OOP由原來的二維變為三維了,由平面變成立體了。從技術上來說,AOP基本上是通過代理機制實現的。
AOP在編程歷史上可以說是里程碑式的,對OOP編程是一種十分有益的補充。
切面編程的一些概念解釋如下。切面(Aspect):其實就是共有功能的實現。如日志切面、權限切面、事務切面等。在實際應用中通常是一個存放共有功能實現的普通Java類,之所以能被AOP容器識別成切面,是在配置中指定的。
通知(Advice):是切面的具體實現。以目標方法為參照點,根據放置的地方不同,可分為前置通知(Before)、后置通知(AfterReturning)、異常通知(AfterThrowing)、最終通知(After)與環繞通知(Around)5種。在實際應用中通常是切面類中的一個方法,具體屬於哪類通知,同樣是在配置中指定的。
連接點(Joinpoint):就是程序在運行過程中能夠插入切面的地點。例如,方法調用、異常拋出或字段修改等,但Spring只支持方法級的連接點。
切入點(Pointcut):用於定義通知應該切入到哪些連接點上。不同的通知通常需要切入到不同的連接點上,這種精准的匹配是由切入點的正則表達式來定義的。
目標對象(Target):就是那些即將切入切面的對象,也就是那些被通知的對象。這些對象中已經只剩下干干凈凈的核心業務邏輯代碼了,所有的共有功能代碼等待AOP容器的切入。
代理對象(Proxy):將通知應用到目標對象之后被動態創建的對象。可以簡單地理解為,代理對象的功能等於目標對象的核心業務邏輯功能加上共有功能。代理對象對於使用者而言是透明的,是程序運行過程中的產物。
織入(Weaving):將切面應用到目標對象從而創建一個新的代理對象的過程。這個過程可以發生在編譯期、類裝載期及運行期,當然不同的發生點有着不同的前提條件。譬如發生在編譯期的話,就要求有一個支持這種AOP實現的特殊編譯器;發生在類裝載期,就要求有一個支持AOP實現的特殊類裝載器;只有發生在運行期,則可直接通過Java語言的反射機制與動態代理機制來動態實現。
切面組件Aspect
切面組件一般是登錄檢查、日志記錄、事務管理等
xml配置中先聲明bean,再在aop:config的aop:aspect中指定為切面
或者用@Aspect注解指定
通知advice
通知就是:在調用目標方法的什么時候調用切面組件的方法,可以是:
前置通知:@Before,方法調用之前
后置通知:@AfterReturning,方法正常返回
異常通知:@AfterThrowing,方法調用過程中出現異常
最終通知:@After,不管方法是正常返回還是出現異常,這個通知都會調用,因此必須這兩種可能的情況
環繞通知:@Around,
切入點
切入點就是要在哪些類的哪些方法上進行切入,可以限定一些類下的所有方法,或者不同類下的一些方法,或者指定類下的指定方法
方法限定表達式:
基本模式:execution([修飾符] 返回值類型 方法名(參數) [異常類型])。具體見Spring Reference 4.3.6版第223頁
execution(public * * (..)):所有 的public方法
execution(* set(..)):以set打頭的任意方法
execution( com.xyz.service.SomeService.(..):SomeService類下的任意方法
execution( com.xyz.service..(..)):service包下的任意類的任意方法
類型限定表達式:
within(com.xyz.service.):service包下的任意類下的任意方法,不包含子包
within(com.xyz.service..):包含子包參考文獻:https://blog.csdn.net/github_34889651/article/details/51321499