SpringAOP原理及應用
SpringAOP原理及應用
一、背景 |
什麽是AOP,英文直譯是Aspect-OrientedProgramming,面向方面編程。從名字也可以看出,如果把我們代碼的執行過程看成一條圓柱體,AOP就是一把刀,從這個圓柱體上選擇任意一點切入進去,獲得其內部的消息。
springAOP概念:
AOP是Aspect Oriented Programing的簡稱,面向切面編程。AOP適合於那些具有橫切邏輯的應用:如性能監測,訪問控制,事務管理、緩存、對象池管理以及日誌記錄。AOP將這些分散在各個業務邏輯中的代碼通過橫向切割的方式抽取到一個獨立的模塊中。AOP 實現的關鍵就在於 AOP 框架自動創建的 AOP 代理,AOP 代理則可分為靜態代理和動態代理兩大類,其中靜態代理是指使用 AOP 框架提供的命令進行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時增強;而動態代理則在運行時借助於 JDK 動態代理、CGLIB 等在內存中“臨時”生成 AOP 動態代理類,因此也被稱為運行時增強。
關鍵詞:
切面(Aspect) :官方的抽象定義為“一個關註點的模塊化,這個關註點可能會橫切多個對象”。
連接點(Joinpoint) :程序執行過程中的某一行為。
通知(Advice) :“切面”對於某個“連接點”所產生的動作。
切入點(Pointcut) :匹配連接點的斷言,在AOP中通知和一個切入點表達式關聯。
目標對象(Target Object) :被一個或者多個切面所通知的對象。
AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。
通知(Advice)類型:
前置通知(Before advice) :在某連接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接點前的執行。ApplicationContext中在<aop:aspect>裏面使用<aop:before>元素進行聲明。
後通知(After advice) :當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裏面使用<aop:after>元素進行聲明。
返回後通知(After return advice) :在某連接點正常完成後執行的通知,不包括拋出異常的情況。ApplicationContext中在<aop:aspect>裏面使用<after-returning>元素進行聲明。
環繞通知(Around advice) :包圍一個連接點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的調用前後完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>裏面使用<aop:around>元素進行聲明。
拋出異常後通知(After throwing advice) : 在方法拋出異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裏面使用<aop:after-throwing>元素進行聲明。
切入點表達式 :如execution(* com.spring.service.*.*(..))
二、原理 |
要了解AOP的實現原理,就不得不提java代理。
1、代理模式簡述: |
代理模式是常用的java設計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事後處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
代碼示例如下:
1 /** 2 3 * 定義一個賬戶接口 4 5 */ 6 7 public interface Count { 8 9 // 修改賬戶方法 10 11 public void updateCount(); 12 13 } 14 15 16 17 ** 18 19 * 委托類(包含業務邏輯) 20 21 */ 22 23 public class CountImpl implements Count { 24 25 26 27 @Override 28 29 public void updateCount() { 30 31 System.out.println("修改賬戶方法..."); 32 33 } 34 35 } 36 37 38 39 /** 40 41 * 這是一個代理類(增強CountImpl實現類) 42 43 */ 44 45 public class CountProxy implements Count { 46 47 private CountImpl countImpl; 48 49 50 51 /** 52 53 * 覆蓋默認構造器 54 55 * 56 57 * @param countImpl 58 59 */ 60 61 public CountProxy(CountImpl countImpl) { 62 63 this.countImpl = countImpl; 64 65 } 66 67 68 69 @Override 70 71 public void updateCount() { 72 73 System.out.println("事務處理之前"); 74 75 // 調用委托類的方法; 76 77 countImpl.updateCount(); 78 79 System.out.println("事務處理之後"); 80 81 } 82 83 }
2、JDK動態代理 |
特點:只能對實現了接口的類生產代理,不能針對類
代碼示例:
/** * 自定義簡單的Invocation,對接口提供的方法進行增強 */ public class MyInvocationHandler implements InvocationHandler { //目標對象 private Object target; /** * 構造方法 * @param target 目標對象 */ public MyInvocationHandler(Object target) { super(); this.target=target; } /** * 執行目標對象的方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在目標方法執行前簡單打印一下 System.out.println("----------before----------"); //執行目標方法對象 Object result=method.invoke(target, args); //在目標方法執行之後簡單打印一下 System.out.println("----------after----------"); return result; } /** * 獲取目標對象的代理對象 * @return 代理對象 */ public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), this.target.getClass().getInterfaces(),this); } } public class ProxyTest{ @Test public void testProxy() throws Throwable{ //實例化目標對象 Count count =new CountImpl (); //實例化Invocation MyInvocationHandler invocationHandler=new MyInvocationHandler(count); //根據目標生成代理對象 Count proxy=(Count)invocationHandler.getProxy(); //調用代理對象方法 proxy. updateCount(); } }
3、CGLIB動態代理: |
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。
CGLIB是一個強大的高性能的代碼生成包。被廣泛的許多AOP框架使用,如Spring的AOP和dynaop,為他們提供方法的interceptor(攔截),最流行的是OR Mapping工具Hibernate也是使用CGLIB來代理單端的single-ended(多對一和一對一)關聯(對集合的延遲抓取是采用其他機制實現)。EsayMock和jMock是通過模仿(moke)對象來測試java代碼的包。他們都是通過使用CGLIB來為那些沒有接口的類創建模仿(moke)對象。
代碼示例:
/** * 使用cglib動態代理 */ public class CountImplCglib implements MethodInterceptor { private Object target; /** * 創建代理對象 * * @param target * @return */ public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回調方法 enhancer.setCallback(this); // 創建代理對象 return enhancer.create(); } @Override // 回調方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事物開始"); proxy.invokeSuper(obj, args); System.out.println("事物結束"); return null; } } public class TestCglib { public static void main(String[] args) { CountImplCglib cglib=new CountImplCglib(); CountImpl countCglib=(CountImpl)cglib.getInstance(new CountImpl()); countCglib.updateCount(); } }
以上只是代理模式實現的代碼,真正在springAOP中的實現更為復雜,若想了解可以參考文章《spring源碼剖析(六)AOP實現原理剖析》。
鏈接http://blog.csdn.net/fighterandknight/article/details/51209822
三、運用添加依賴: |
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.0.5.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.1</version> </dependency>
新增一個業務類: |
public class Car { public void go(){ System.out.println("go go go!"); } }
寫一個切面類: |
public class CarLogger { public void beforeRun(){ System.out.println("car is going to run"); } public void afterRun(){ System.out.println("car is running"); } }
新增配置文件: |
<!-- 把兩個bean加入spring容器 --> <bean id="car" class="com.wowo.spring_aop_demo1.Car"/> <bean id="logger" class="com.wowo.spring_aop_demo1.CarLogger" /> <!--AOP配置--> <aop:config> <!-- 指明切面實現bean logger --> <aop:aspect ref="logger"> <!-- 指明切入點 --> <aop:pointcut expression="execution(* com.wowo.spring_aop_demo1.Car.go(..))" id="go"/> <!-- 配置通知 --> <aop:before pointcut-ref="go" method="beforeRun" /> <aop:after pointcut-ref="go" method="afterRun" /> </aop:aspect> </aop:config>
execution(* com.wowo.spring_aop_demo1.Car.go(..))是一個AspectJ切點表達式,execution表示在執行時觸發,後面的*表示任意類型的返回值,com.wowo.spring_aop_demo1.Car指的是切點所在的類,go(..)是方法名,..表示任意參數。若想詳細了解,參考文章《AspectJ的切入點表達式---execution表達式詳解》。
鏈接http://blog.csdn.net/lk7688535/article/details/51989746
四、結論 |
AOP是和IOC並稱的Spring兩大核心之一。Spring的AOP實現比較簡化,但借助AspectJ可以對AOP實現的更徹底。
SpringAOP原理及應用