一、什麼是 AOP。
AOP(Aspect Orient Programming),也就是面向切面編程。可以這樣理解,面向對象編程(OOP)是從靜態角度考慮程序結構,面向切面編程(AOP)是從動態角度考慮程序運行過程。
二、AOP 的作用。
常常通過 AOP 來處理一些具有橫切性質的系統性服務,如事物管理、安全檢查、緩存、對象池管理等,AOP 已經成爲一種非常常用的解決方案。
三、AOP 的實現原理。
如圖:AOP 實際上是由目標類的代理類實現的。AOP 代理其實是由 AOP 框架動態生成的一個對象,該對象可作爲目標對象使用。AOP 代理包含了目標對象的全部方法,但 AOP 代理中的方法與目標對象的方法存在差異,AOP 方法在特定切入點添加了增強處理,並回調了目標對象的方法。
四、Spring 中對 AOP 的支持
Spring 中 AOP 代理由 Spring 的 IoC 容器負責生成、管理,其依賴關係也由 IoC 容器負責管理。因此,AOP 代理可以直接使用容器中的其他 Bean 實例作爲目標,這種關係可由 IoC 容器的依賴注入提供。Spring 默認使用 Java 動態代理來創建 AOP 代理, 這樣就可以爲任何接口實例創建代理了。當需要代理的類不是代理接口的時候, Spring 自動會切換爲使用 CGLIB 代理,也可強制使用 CGLIB。
AOP 編程其實是很簡單的事情。縱觀 AOP 編程, 其中需要程序員參與的只有三個部分:
定義普通業務組件。
定義切入點,一個切入點可能橫切多個業務組件。
定義增強處理,增強處理就是在 AOP 框架爲普通業務組件織入的處理動作。
所以進行 AOP 編程的關鍵就是定義切入點和定義增強處理。一旦定義了合適的切入點和增強處理,AOP 框架將會自動生成 AOP 代理,即:代理對象的方法 = 增強處理 + 被代理對象的方法。
五、Spring 中 AOP 的實現。
Spring 有如下兩種選擇來定義切入點和增強處理。
基於 Annotation 的“零配置”方式:使用@Aspect、@Pointcut等 Annotation 來標註切入點和增強處理。
基於 XML 配置文件的管理方式:使用 Spring 配置文件來定義切入點和增強點。
1、基於 Annotation 的“零配置”方式。
(1)、首先啓用 Spring 對 @AspectJ 切面配置的支持。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop-3.0.xsd"> <!-- 啓動對@AspectJ註解的支持 --> <aop:aspectj-autoproxy/> </beans>
如果不打算使用 Spring 的 XML Schema 配置方式,則應該在 Spring 配置文件中增加如下片段來啓用@AspectJ 支持。
<!-- 啓用@AspectJ 支持 --> <bean class="org.springframeword.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
(2)、定義切面 Bean。
當啓動了@AspectJ 支持後,只要在 Spring 容器中配置一個帶@Aspect 註釋的 Bean, Spring 將會自動識別該 Bean 並作爲切面處理。
// 使用@Aspect 定義一個切面類 @Aspect public class LogAspect { // 定義該類的其他內容 ... }
(3)、定義 Before 增強處理。
// 定義一個切面 @Aspect public class BeforeAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作爲切入點 @Before("execution(* com.wicresoft.app.service.impl.*.*(..))") public void authorith(){ System.out.println("模擬進行權限檢查。"); } } 上面使用@Before Annotation 時,直接指定了切入點表達式,指定匹配
關於這個表達式的規則如下圖。
(4)、定義 AfterReturning 增強處理。
// 定義一個切面 @Aspect public class AfterReturningAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作爲切入點 @AfterReturning(returning="rvt", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))") public void log(Object rvt) { System.out.println("模擬目標方法返回值:" + rvt); System.out.println("模擬記錄日誌功能..."); } }
(5)、定義 AfterThrowing 增強處理。
// 定義一個切面 @Aspect public class AfterThrowingAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作爲切入點 @AfterThrowing(throwing="ex", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))") public void doRecoverActions(Throwable ex) { System.out.println("目標方法中拋出的異常:" + ex); System.out.println("模擬拋出異常後的增強處理..."); } }
(6)、定義 After 增強處理。
After 增強處理與AfterReturning 增強處理有點相似,但也有區別:
AfterReturning 增強處理處理只有在目標方法成功完成後纔會被織入。
After 增強處理不管目標方法如何結束(保存成功完成和遇到異常中止兩種情況),它都會被織入。
// 定義一個切面 @Aspect public class AfterAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作爲切入點 @After("execution(* com.wicresoft.app.service.impl.*.*(..))") public void release() { System.out.println("模擬方法結束後的釋放資源..."); } }
(7)、Around 增強處理
Around 增強處理近似等於 Before 增強處理和 AfterReturning 增強處理的總和。它可改變執行目標方法的參數值,也可改變目標方法之後的返回值。
// 定義一個切面 @Aspect public class AroundAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下所有類的所有方法作爲切入點 @Around("execution(* com.wicresoft.app.service.impl.*.*(..))") public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable { System.out.println("執行目標方法之前,模擬開始事物..."); // 執行目標方法,並保存目標方法執行後的返回值 Object rvt = jp.proceed(new String[]{"被改變的參數"}); System.out.println("執行目標方法之前,模擬結束事物..."); return rvt + "新增的內容"; } }
(8)、訪問目標方法的參數。
訪問目標方法最簡單的做法是定義增強處理方法時將第一個參數定義爲 JoinPoint 類型,當該增強處理方法被調用時,該 JoinPoint 參數就代表了織入增強處理的連接點。JoinPoint 裏包含了如下幾個常用方法。
Object[] getArgs(): 返回執行目標方法時的參數。
Signature getSignature(): 返回被增強的方法的相關信息。
Object getTarget(): 返回被織入增強處理的目標對象。
Object getThis(): 返回 AOP 框架爲目標對象生成的代理對象。
提示:當時使用 Around 處理時,我們需要將第一個參數定義爲 ProceedingJoinPoint 類型,該類型是 JoinPoint 類型的子類。
(9)、定義切入點。
所謂切入點,其實質就是爲一個切入點表達式起一個名稱,從而允許在多個增強處理中重用該名稱。
Spring 切入點定義包含兩個部分:
一個切入點表達式。
一個包含名字和任意參數的方法簽名。
// 使用@Pointcut Annotation 時指定切入點表達式 @pointcut("execution * transfer(..)") // 使用一個返回值爲void,方法體爲空的方法來命名切入點 private void anyOldTransfer(){} // 使用上面定義的切入點 @AfterReturning(pointcut="anyOldTransfer()", returning="reVal") public void writeLog(String msg, Object reVal){ ... }
2、基於 XML 配置文件的管理方式。
不配置切入點
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop-3.0.xsd"> <aop:config> <!-- 將 fourAdviceBean 轉換成切面 Bean, 切面 Bean 的新名稱爲:fourAdviceAspect,指定該切面的優先級爲2 --> <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2"> <!-- 定義個After增強處理,直接指定切入點表達式,以切面 Bean 中的 Release() 方法作爲增強處理方法 --> <aop:after pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> <!-- 定義個Before增強處理,直接指定切入點表達式,以切面 Bean 中的 authority() 方法作爲增強處理方法 --> <aop:before pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="authority" /> <!-- 定義個AfterReturning增強處理,直接指定切入點表達式,以切面 Bean 中的 log() 方法作爲增強處理方法 --> <aop:after-returning pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="log" /> <!-- 定義個Around增強處理,直接指定切入點表達式,以切面 Bean 中的 processTx() 方法作爲增強處理方法 --> <aop:around pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="processTx" /> </aop:aspect> </aop:config> <!-- 省略各個Bean 的配置 --> <!-- ... --> </beans>
配置切入點
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop-3.0.xsd"> <aop:config> <!-- 定義一個切入點,myPointcut,直接知道它對應的切入點表達式 --> <aop:pointcut id="myPointcut" expression="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> <aop:aspect id="afterThrowingAdviceAspect" ref="afterThrowingAdviceBean" order="1"> <!-- 使用上面定於切入點定義增強處理 --> <!-- 定義一個AfterThrowing 增強處理,指定切入點以切面 Bean 中的 doRecovertyActions() 方法作爲增強處理方法 --> <aop:after-throwing pointcut-ref="myPointcut" method="doRecovertyActions" throwing="ex" /> </aop:aspect> </aop:config> <!-- 省略各個Bean 的配置 --> <!-- ... --> </beans>