Spring框架——AOP(面向切面編程)詳解
1 AOP概述
●AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論,是對傳統 OOP(Object-Oriented Programming,面向對象編程)的補充。
●AOP編程操作的主要對象是切面(aspect),而切面模塊化橫切關註點。
●在應用AOP編程時,仍然需要定義公共功能,但可以明確的定義這個功能應用在哪裏,以什麽方式應用,並且不必修改受影響的類。這樣一來橫切關註點就被模塊化到特殊的類裏——這樣的類我們通常稱之為“切面”。
●AOP的好處:
○每個事物邏輯位於一個位置,代碼不分散,便於維護和升級
○業務模塊更簡潔,只包含核心業務代碼
2 AOP術語
2.1 橫切關註點
從每個方法中抽取出來的同一類非核心業務。(抽離到方法中處理非核心業務)
2.2 切面(Aspect)
封裝橫切關註點信息的類,每個關註點體現為一個通知方法。
2.3 通知(Advice)
切面必須要完成的各個具體工作
2.4 目標(Target)
被通知的對象
2.5 代理(Proxy)
向目標對象應用通知之後創建的代理對象
2.6 連接點(Joinpoint)
橫切關註點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用後、方法捕獲到異常後等。
在應用程序中可以使用橫縱兩個坐標來定位一個具體的連接點:
2.7 切入點(pointcut):
定位連接點的方式。每個類的方法中都包含多個連接點,所以連接點是類中客觀存在的事物。如果把連接點看作數據庫中的記錄,那麽切入點就是查詢條件——AOP可以通過切入點定位到特定的連接點。
切點通過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作為連接點的查詢條件。
3 AspectJ
3.1 簡介
AspectJ:Java社區裏最完整最流行的AOP框架。
在Spring2.0以上版本中,可以使用基於AspectJ註解或基於XML配置的AOP。
3.2 在Spring中啟用AspectJ註解支持
①導入JAR 包
●aopalliance.jar
●aspectj.weaver.jar
●spring-aspects.jar
②引入aop名稱空間
③配置
<aop:aspectj-autoproxy>
當Spring IOC容器偵測到bean配置文件中的<aop:aspectj-autoproxy>元素時,會自動為與AspectJ切面匹配的bean創建代理
3.3 用AspectJ註解聲明切面
①要在Spring中聲明AspectJ切面,只需要在IOC容器中將切面聲明為bean實例。②當在Spring IOC容器中初始化AspectJ切面之後,Spring IOC容器就會為那些與 AspectJ切面相匹配的bean創建代理。
③在AspectJ註解中,切面只是一個帶有@Aspect註解的Java類,它往往要包含很多通知。
④通知是標註有某種註解的簡單的Java方法。
⑤AspectJ支持5種類型的通知註解:
[1]@Before:前置通知,在方法執行之前執行
[2]@After:後置通知,在方法執行之後執行
[3]@AfterRunning:返回通知,在方法返回結果之後執行
[4]@AfterThrowing:異常通知,在方法拋出異常之後執行
[5]@Around:環繞通知,圍繞著方法執行
4 切入點表達式
4.1 作用
通過表達式的方式定位一個或多個具體的連接點。
4.2 語法細節
①切入點表達式的語法格式
execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表])) |
②舉例說明
表達式 |
execution(* com.atguigu.spring.ArithmeticCalculator.*(..)) |
含義 |
ArithmeticCalculator接口中聲明的所有方法。 第一個“*”代表任意修飾符及任意返回值。 第二個“*”代表任意方法。 “..”匹配任意數量、任意類型的參數。 若目標類、接口與該切面類在同一個包中可以省略包名。 |
表達式 |
execution(public * ArithmeticCalculator.*(..)) |
含義 |
ArithmeticCalculator接口的所有公有方法 |
表達式 |
execution(public double ArithmeticCalculator.*(..)) |
含義 |
ArithmeticCalculator接口中返回double類型數值的方法 |
表達式 |
execution(public double ArithmeticCalculator.*(double, ..)) |
含義 |
第一個參數為double類型的方法。 “..” 匹配任意數量、任意類型的參數。 |
表達式 |
execution(public double ArithmeticCalculator.*(double, double)) |
含義 |
參數類型為double,double類型的方法 |
③在AspectJ中,切入點表達式可以通過 “&&”、“||”、“!”等操作符結合起來。
表達式 |
execution (* *.add(int,..)) || execution(* *.sub(int,..)) |
含義 |
任意類中第一個參數為int類型的add方法或sub方法 |
4.3 切入點表達式應用到實際的切面類中
5 當前連接點細節
5.1 概述
切入點表達式通常都會是從宏觀上定位一組方法,和具體某個通知的註解結合起來就能夠確定對應的連接點。那麽就一個具體的連接點而言,我們可能會關心這個連接點的一些具體信息,例如:當前連接點所在方法的方法名、當前傳入的參數值等等。這些信息都封裝在JoinPoint接口的實例對象中。
5.2 JoinPoint
6 通知
6.1 概述
l 在具體的連接點上要執行的操作。
l 一個切面可以包括一個或者多個通知。
l 通知所使用的註解的值往往是切入點表達式。
6.2 前置通知
l 前置通知:在方法執行之前執行的通知
l 使用@Before註解
6.3 後置通知
l 後置通知:後置通知是在連接點完成之後執行的,即連接點返回結果或者拋出異常的時候
l 使用@After註解
6.4 返回通知
l 返回通知:無論連接點是正常返回還是拋出異常,後置通知都會執行。如果只想在連接點返回的時候記錄日誌,應使用返回通知代替後置通知。
l 使用@AfterReturning註解
l 在返回通知中訪問連接點的返回值
- 在返回通知中,只要將returning屬性添加到@AfterReturning註解中,就可以訪問連接點的返回值。該屬性的值即為用來傳入返回值的參數名稱
- 必須在通知方法的簽名中添加一個同名參數。在運行時Spring AOP會通過這個參數傳遞返回值
- 原始的切點表達式需要出現在pointcut屬性中
6.5 異常通知
l 異常通知:只在連接點拋出異常時才執行異常通知
l 將throwing屬性添加到@AfterThrowing註解中,也可以訪問連接點拋出的異常。Throwable是所有錯誤和異常類的頂級父類,所以在異常通知方法可以捕獲到任何錯誤和異常。
l 如果只對某種特殊的異常類型感興趣,可以將參數聲明為其他異常的參數類型。然後通知就只在拋出這個類型及其子類的異常時才被執行
6.6 環繞通知
l 環繞通知是所有通知類型中功能最為強大的,能夠全面地控制連接點,甚至可以控制是否執行連接點。
l 對於環繞通知來說,連接點的參數類型必須是ProceedingJoinPoint。它是 JoinPoint的子接口,允許控制何時執行,是否執行連接點。
l 在環繞通知中需要明確調用ProceedingJoinPoint的proceed()方法來執行被代理的方法。如果忘記這樣做就會導致通知被執行了,但目標方法沒有被執行。
l 註意:環繞通知的方法需要返回目標方法執行之後的結果,即調用 joinPoint.proceed();的返回值,否則會出現空指針異常。
6.7 重用切入點定義
l 在編寫AspectJ切面時,可以直接在通知註解中書寫切入點表達式。但同一個切點表達式可能會在多個通知中重復出現。
l 在AspectJ切面中,可以通過@Pointcut註解將一個切入點聲明成簡單的方法。切入點的方法體通常是空的,因為將切入點定義與應用程序邏輯混在一起是不合理的。
l 切入點方法的訪問控制符同時也控制著這個切入點的可見性。如果切入點要在多個切面中共用,最好將它們集中在一個公共的類中。在這種情況下,它們必須被聲明為public。在引入這個切入點時,必須將類名也包括在內。如果類沒有與這個切面放在同一個包中,還必須包含包名。
l 其他通知可以通過方法名稱引入該切入點
6.8 指定切面的優先級
l 在同一個連接點上應用不止一個切面時,除非明確指定,否則它們的優先級是不確定的。
l 切面的優先級可以通過實現Ordered接口或利用@Order註解指定。
l 實現Ordered接口,getOrder()方法的返回值越小,優先級越高。
l 若使用@Order註解,序號出現在註解中
6.9註意:
上面的AOP都是通過註解實現的,AOP實際上也可以通過xml配置的方式實現!
<!-- 1.將需要加載到IOC容器中的bean配置好 --> <bean id="logAspect" class="com.neuedu.aop.proxy.LogAspect"></bean> <bean id="txAspect" class="com.neuedu.aop.target.TxAspect"></bean> <bean id="calculator" class="com.neuedu.aop.target.MathCalculatorImpl"></bean> <!-- 2.配置AOP,需要導入AOP名稱空間 --> <aop:config> <!-- 聲明切入點表達式 --> <aop:pointcut expression="execution(* com.neuedu.aop.target.MathCalculatorImpl.*(..))" id="myPointCut"/> <!-- 配置日誌切面類,引用前面的類 ,通過order屬性控制優先級--> <aop:aspect ref="logAspect" order="25"> <!-- 通過method屬性指定切面類的切面方法,通過pointcut-ref指定切入點表達式 --> <aop:before method="showBeginLog" pointcut-ref="myPointCut"/> <aop:after method="showAfterLog" pointcut-ref="myPointCut"/> <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/> <aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/> <aop:around method="around" pointcut-ref="myPointCut"/> </aop:aspect> <!-- 配置事務切面類,引用前面的類 --> <aop:aspect ref="txAspect" order="20"> <aop:around method="around" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config>
需要知道的是:事務的管理是和AOP是有很大關系的,即聲明式事務的底層是用事務實現的!
Spring框架——AOP(面向切面編程)詳解