AOP 應用:
1. 監控系統重要API的調用事件,用來監控系統的性能。
2.Authentication 權限
3. Caching 緩存
4. Context passing 內容傳遞
5. Error handling 錯誤處理
6. Lazy loading 懶加載
7. Debugging 調試
8. logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準
9. Performance optimization 性能優化
10. Persistence 持久化
11. Resource pooling 資源池
12. Synchronization 同步
13. Transactions 事務
......html
相信只要使用過Spring框架的,你們對於AOP都不陌生,尤爲提起它就能馬上隨口說出,通常用在日誌處理、異常處理、權限驗證等方面。但剛開始接觸不免會有各類各樣的疑惑,今天抽時間,按照以前的理解整理了一份關於Spring AOP的簡單教程,但願可以幫助你們儘快的瞭解它的實現過程及原理。首先來明確幾個概念:java
JointPoint正則表達式
系統在運行以前,AOP的功能模塊須要織入到OOP的功能模塊中。要進行這種織入過程,須要知道在系統的哪些功能點上進行織入操做,這些將要在其上進行織入操做的系統功能點就稱爲JointPoint。如某方法調用的時候或者處理異常的時候,在Spring AOP中,一個鏈接點老是表示一個方法的執行。常見的幾種類型的JoinPoint:spring
Ø 方法調用:當某個方法被調用的時候所處的程序執行點;緩存
Ø 方法執行:該類型表示的是某個方法內部執行開始時的點,應該與方法調用相區分;性能優化
Ø 構造方法調用:程序執行過程當中對某個對象調用其構造方法進行初始化時的點;app
Ø 構造方法執行:它與構造方法調用關係如同方法調用與方法執行間的關係;框架
Ø 字段設置:對象的某個屬性經過setter方法被設置或直接被設置的執行點;ide
Ø 字段獲取:某個對象相應屬性被訪問的執行點;模塊化
Ø 異常處理執行:某些類型異常拋出後,對應的異常處理邏輯執行點;
Ø 類初始化:類中某些靜態類型或靜態塊的初始化時的執行點。
Pointcut
Pointcut表明的是JoinPoint的表述方式。在將橫切邏輯織入當前系統的過程當中,雖然知道須要在哪些功能點上織入AOP的功能模塊,但須要一種表達方法。Pointcut和一個切入點表達式關聯,並在知足這個切入點的Joinpoint上運行。目前一般使用的Pointcut方式有如下幾種:
Ø 直接指定Joinpoint所在的方法名稱;
Ø 正則表達式,Spring的AOP支持該種方式;
Ø 使用特定的Pointcut表述語言,Spring 2.0後支持該方式。
Advice
Advice是單一橫切關注點邏輯的載體,它表明將會織入到JoinPoint的橫切邏輯。在切面的某個特定的鏈接點上執行的邏輯。根據它在Joinpoint位置執行時機的差別或完成功能的不一樣,可分爲如下幾種形式:
Ø Before Advice:在Joinpoint指定位置以前執行的Advice類型,能夠採用它來作一些系統的初始化工做,如設置系統初始值,獲取必要系統資源等。
Ø After Advice:在相應鏈接點以後執行的Advice類型,它還能夠細分爲如下三種:
² After Returning Advice:只有當前Joinpoint處執行流程正常完成後,它纔會執行;
² After throwing Advice:在當前Joinpoint執行過程當中拋出異常的狀況下會執行;
² After Advice:該類型的Advice無論JoinPoint處執行流程是正常仍是拋出異常都會執行。
Ø Around Advice:對附加其上的Joinpoint進行包裹,能夠在joinpoint以前和以後都指定相應的邏輯,甚至中斷或忽略joinpoint處原來程序流程的執行。
Aspect
它是對系統中橫切關注點邏輯進行模塊化封裝的AOP概念實體,它能夠包含多個Pointcut以及相關的Advice定義。
織入器
通過織入過程後,以Aspect模塊化的橫切關注點纔會集成到oop的現存系統中,而完成織入過程實體稱爲織入器。Spring中使用一組類來完成最終的織入操做,ProxyFactory類是Spring AOP最通用的織入器。
目標對象
符合Pointcut所指定的條件,將在織入過程當中被織入橫切邏輯的對象,稱之爲目標對象。
單看上述的概念,可能會以爲有點眼花繚亂,其實經過一個簡單的AOP的實例便可以幫助咱們很快的瞭解其內部的機制。其實對於方法攔截有不一樣的實現方式,經常使用的即有直接採用Spring提供的各類Advice進行攔截,另外一種則是採用MethodInterceptor方式進行攔截。
Spring提供的Advice攔截方式
定義一個邏輯接口IBusinessLogic:
package com.wow.asc.aop; public interface IBusinessLogic { public void foo(); public void bar() throws BusinessLogicException; public long time(); }
其中有一個BusinessLogicException異常,它用於後面對於ThrowsAdvice進行檢驗的實例,在此定義爲:
package com.wow.asc.aop; public class BusinessLogicException extends Exception { }
對於該業務邏輯的實現BusinessLogic,以下所示:
package com.wow.asc.aop; public class BusinessLogic implements IBusinessLogic { @Override public void foo() { System.out.println("Inside BusinessLogic.foo()"); } @Override public void bar() throws BusinessLogicException { System.out.println("Inside BusinessLogic.bar()"); throw new BusinessLogicException(); } /* * 返回該方法執行的時間 */ @Override public long time() { System.out.println("Inside BusinessLogic.time()"); long startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++); long endTime = System.currentTimeMillis(); return (endTime - startTime); } }
在完成上述業務邏輯編碼後,接下來將進行更多的橫切插入點的設計,如在方法執行前或返回時、拋出異常時進行各類處理。對於Advice的寫法以下所示:
package com.wow.asc.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /* * 表示一個在方法執行前進行攔截的一個Advice */ public class TracingBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("execute before (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")"); } } package com.wow.asc.aop; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; /* * 表示一個在方法返回時進行攔截的Advice */ public class TracingAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getDeclaringClass().getName() + "." + method.getName() + "spend time: " + returnValue); System.out.println("execute after (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")"); } } package com.wow.asc.aop; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; /* * 表示一個異常拋出時進行攔截的Advice */ public class TracingThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Method method, Object[] args, Object target, Throwable subclass) { System.out.println( "Logging that a " + subclass + "Exception was thrown."); } }
在設計完上述的代碼及邏輯後,便可以經過applicationContext.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" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://localhost:8080/schema/www.springframework.org/schema/context/spring-context-2.5.xsd"> <bean id="businessLogic" class="com.wow.asc.aop.BusinessLogic" /> <bean id="businessLogicBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.wow.asc.aop.IBusinessLogic</value> </property> <property name="target"> <ref local="businessLogic"/> </property> <property name="interceptorNames"> <list> <value>theTracingBeforeAdvisor</value> <value>theTracingAfterAdvisor</value> <value>theTracingThrowsAdvisor</value> </list> </property> </bean> <bean id="theTracingBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingBeforeAdvice"/> </property> <property name="pattern"> <value>.*</value> </property> </bean> <bean id="theTracingAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingAfterAdvice"/> </property> <property name="pattern"> <value>.*time.*</value> </property> </bean> <bean id="theTracingThrowsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingThrowsAdvice"/> </property> <property name="pattern"> <value>.*bar.*</value> </property> </bean> <bean id="theTracingBeforeAdvice" class="com.wow.asc.aop.TracingBeforeAdvice"/> <bean id="theTracingAfterAdvice" class="com.wow.asc.aop.TracingAfterAdvice"/> <bean id="theTracingThrowsAdvice" class="com.wow.asc.aop.TracingThrowsAdvice"/> </beans>
經過上述的配置,咱們能夠看出咱們將IBusinessLogic作爲代理接口,同時它的真正的目標類是BusinesssLogic。同時會對全部進入方法以前採用TracingBeforeAdvice進行攔截,進行方法前的預處理;對time方法採用TracingAfterAdvice進行攔截,進行方法返回後的處理;對於bar則採用TracingThrowsAdvice進行攔截,當方法返回BusinessLogicException時進行相應的處理。
在配置完上述類的依賴關係及須要攔截的方法後,便可以編寫客戶端程序來調用,查看它的運行機制。客戶端調用代碼:
package com.wow.asc.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.wow.asc.aop.BusinessLogicException; import com.wow.asc.aop.IBusinessLogic; public class AOPTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); IBusinessLogic ibl = (IBusinessLogic)ac.getBean("businessLogicBean"); ibl.foo(); try { ibl.bar(); } catch (BusinessLogicException e) { System.out.println("Caught BusinessLogicException"); } ibl.time(); } }
經過運行結果來詳細的瞭解下,看是否真正的如上所述,會在方法前、後及異常拋出時可以攔截並進行相應處理。結果以下:
一、execute before (by com.wow.asc.aop.IBusinessLogic.foo) 二、Inside BusinessLogic.foo() 三、execute before (by com.wow.asc.aop.IBusinessLogic.bar) 四、Inside BusinessLogic.bar() 五、Logging that a com.wow.asc.aop.BusinessLogicExceptionException was thrown. 六、Caught BusinessLogicException 七、execute before (by com.wow.asc.aop.IBusinessLogic.time) 八、Inside BusinessLogic.time() 九、com.wow.asc.aop.IBusinessLogic.timespend time: 46 十、execute after (by com.wow.asc.aop.IBusinessLogic.time)
其實經過一、三、7行能夠很是清晰的瞭解到,每一個方法在執行前都被TracingBeforeAdvice攔截到,並執行了預處理。五、6行表示當調用的是bar方法時,會被TracingThrowsAdvice攔截,當有異常拋出時,會執行相應的處理;八、九、10行則表示當調用的是time方法,返回時會被TracingAfterAdvice攔截,對其返回值進行處理。
MethodInterceptor攔截方式
採用該種方式進行攔截,須要實現一個繼承自MethodInterceptor的類,並將該類註冊至spring Context中,具體以下:
package com.wow.asc.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) { Object result = null; StringBuffer info = new StringBuffer(); info.append("intercept the method: "); info.append(invocation.getMethod().getDeclaringClass(). getName()); info.append("."); info.append(invocation.getMethod().getName()); System.out.println("start " + info.toString()); try { result = invocation.proceed(); } catch (Throwable e) { e.printStackTrace(); } finally { System.out.println("end " + info.toString()); } return result; } }
對於類的裝配,其實和上面的很是相似,示例:
<bean id="testBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.wow.asc.aop.IBusinessLogic</value> </property> <property name="target"> <ref local="businessLogic"/> </property> <property name="interceptorNames"> <list> <value>myInterceptor</value> </list> </property> </bean> <bean id="myInterceptor" class="com.wow.asc.aop.MyInterceptor"/>
再經過客戶端進行調用,可獲得運行結果,從結果來分析能夠看出它在方法執行的前、後均添加了相應的日誌。
start intercept the method: com.wow.asc.aop.IBusinessLogic.foo Inside BusinessLogic.foo() end intercept the method: com.wow.asc.aop.IBusinessLogic.foo start intercept the method: com.wow.asc.aop.IBusinessLogic.time Inside BusinessLogic.time() end intercept the method: com.wow.asc.aop.IBusinessLogic.time
至此,採用兩種不一樣方式實現的AOP就結束了,但願你們可以體會到其中的奧妙。
參考AOP資料:
http://www.zabada.com/technology/aop-example.html
http://onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=1