AOP(Aspect Orient Programming),也就是面向切面編程。能夠這樣理解,面向對象編程(OOP)是從靜態角度考慮程序結構,面向切面編程(AOP)是從動態角度考慮程序運行過程。java
經常經過 AOP 來處理一些具備橫切性質的系統性服務,如事物管理、安全檢查、緩存、對象池管理等,AOP 已經成爲一種很是經常使用的解決方案。程序員
如圖:AOP 其實是由目標類的代理類實現的。AOP 代理實際上是由 AOP 框架動態生成的一個對象,該對象可做爲目標對象使用。AOP 代理包含了目標對象的所有方法,但 AOP 代理中的方法與目標對象的方法存在差別,AOP 方法在特定切入點添加了加強處理,並回調了目標對象的方法。spring
Spring 中 AOP 代理由 Spring 的 IoC 容器負責生成、管理,其依賴關係也由 IoC 容器負責管理。所以,AOP 代理能夠直接使用容器中的其餘 Bean 實例做爲目標,這種關係可由 IoC 容器的依賴注入提供。Spring 默認使用 Java 動態代理來建立 AOP 代理, 這樣就能夠爲任何接口實例建立代理了。當須要代理的類不是代理接口的時候, Spring 自動會切換爲使用 CGLIB 代理,也可強制使用 CGLIB。 express
AOP 編程實際上是很簡單的事情。縱觀 AOP 編程, 其中須要程序員參與的只有三個部分:編程
定義普通業務組件。緩存
定義切入點,一個切入點可能橫切多個業務組件。安全
定義加強處理,加強處理就是在 AOP 框架爲普通業務組件織入的處理動做。app
因此進行 AOP 編程的關鍵就是定義切入點和定義加強處理。一旦定義了合適的切入點和加強處理,AOP 框架將會自動生成 AOP 代理,即:代理對象的方法 = 加強處理 + 被代理對象的方法。框架
Spring 有以下兩種選擇來定義切入點和加強處理。ide
基於 Annotation 的「零配置」方式:使用@Aspect、@Pointcut等 Annotation 來標註切入點和加強處理。
基於 XML 配置文件的管理方式:使用 Spring 配置文件來定義切入點和加強點。
<?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" />
當啓動了@AspectJ 支持後,只要在 Spring 容器中配置一個帶@Aspect 註釋的 Bean, Spring 將會自動識別該 Bean 並做爲切面處理。
// 使用@Aspect 定義一個切面類 @Aspect public class LogAspect { // 定義該類的其餘內容 ... }
// 定義一個切面 @Aspect public class BeforeAdviceTest { // 匹配 com.wicresoft.app.service.impl 包下全部類的全部方法做爲切入點 @Before("execution(* com.wicresoft.app.service.impl.*.*(..))") public void authorith(){ System.out.println("模擬進行權限檢查。"); } } 上面使用@Before Annotation 時,直接指定了切入點表達式,指定匹配
關於這個表達式的規則以下圖。
// 定義一個切面 @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("模擬記錄日誌功能..."); } }
// 定義一個切面 @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("模擬拋出異常後的加強處理..."); } }
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("模擬方法結束後的釋放資源..."); } }
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 + "新增的內容"; } }
訪問目標方法最簡單的作法是定義加強處理方法時將第一個參數定義爲 JoinPoint 類型,當該加強處理方法被調用時,該 JoinPoint 參數就表明了織入加強處理的鏈接點。JoinPoint 裏包含了以下幾個經常使用方法。
Object[] getArgs(): 返回執行目標方法時的參數。
Signature getSignature(): 返回被加強的方法的相關信息。
Object getTarget(): 返回被織入加強處理的目標對象。
Object getThis(): 返回 AOP 框架爲目標對象生成的代理對象。
提示:當時使用 Around 處理時,咱們須要將第一個參數定義爲 ProceedingJoinPoint 類型,該類型是 JoinPoint 類型的子類。
所謂切入點,其實質就是爲一個切入點表達式起一個名稱,從而容許在多個加強處理中重用該名稱。
Spring 切入點定義包含兩個部分:
一個切入點表達式。
一個包含名字和任意參數的方法簽名。
// 使用@Pointcut Annotation 時指定切入點表達式 @pointcut("execution * transfer(..)") // 使用一個返回值爲void,方法體爲空的方法來命名切入點 private void anyOldTransfer(){} // 使用上面定義的切入點 @AfterReturning(pointcut="anyOldTransfer()", returning="reVal") public void writeLog(String msg, Object reVal){ ... }
不配置切入點
<?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>