1. 程式人生 > >AOP 切面程式設計

AOP 切面程式設計

此前對於AOP的使用僅限於宣告式事務,除此之外在實際開發中也沒有遇到過與之相關的問題。最近專案中遇到了以下幾點需求,仔細思考之後,覺得采用AOP來解決。一方面是為了以更加靈活的方式來解決問題,另一方面是藉此機會深入學習SpringAOP相關的內容。本文是權當本人的自己AOP學習筆記,以下需求不用AOP肯定也能解決,至於是否牽強附會,仁者見仁智者見智。
  1. 對部分函式的呼叫進行日誌記錄,用於觀察特定問題在執行過程中的函式呼叫情況
  2. 監控部分重要函式,若丟擲指定的異常,需要以簡訊或郵件方式通知相關人員
  3. 金控部分重要函式的執行時間

    事實上,以上需求沒有AOP也能搞定,只是在實現過程中比較鬱悶擺了。

  1. 需要列印日誌的函式分散在各個包中,只能找到所有的函式體,手動新增日誌。然而這些日誌都是臨時的,待問題解決之後應該需要清除列印日誌的程式碼,只能再次手動清除^_^!
  2. 類似1的情況,需要捕獲異常的地方太多,如果手動新增時想到很可能明天又要手動清除,只能再汗。OK,該需求相對比較固定,屬於長期監控的範疇,並不需求臨時新增後再清除。然而,客戶某天要求,把其中20%的異常改為簡訊提醒,剩下的80%改用郵件提醒。改之,兩天後,客戶抱怨簡訊太多,全部改成郵件提醒...
  3. 該需求通常用於監控某些函式的執行時間,用以判斷系統執行慢的瓶頸所在。瓶頸被解決之後,煩惱同情況1


終於下定決心,採用AOP來解決!程式碼如下:

    切面類TestAspect

Java程式碼  收藏程式碼
  1. package com.spring.aop;  
  2. /** 
  3.  * 切面 
  4.  * 
  5.  */
  6. publicclass TestAspect {  
  7.     publicvoid doAfter(JoinPoint jp) {  
  8.         System.out.println("log Ending method: "
  9.                 + jp.getTarget().getClass().getName() + "."
  10.                 + jp.getSignature().getName());  
  11.     }  
  12.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
  13.         long time = System.currentTimeMillis();  
  14.         Object retVal = pjp.proceed();  
  15.         time = System.currentTimeMillis() - time;  
  16.         System.out.println("process time: "
     + time + " ms");  
  17.         return retVal;  
  18.     }  
  19.     publicvoid doBefore(JoinPoint jp) {  
  20.         System.out.println("log Begining method: "
  21.                 + jp.getTarget().getClass().getName() + "."
  22.                 + jp.getSignature().getName());  
  23.     }  
  24.     publicvoid doThrowing(JoinPoint jp, Throwable ex) {  
  25.         System.out.println("method " + jp.getTarget().getClass().getName()  
  26.                 + "." + jp.getSignature().getName() + " throw exception");  
  27.         System.out.println(ex.getMessage());  
  28.     }  
  29.     privatevoid sendEx(String ex) {  
  30.         //TODO 傳送簡訊或郵件提醒
  31.     }  
  32. }   
Java程式碼  收藏程式碼
  1. package com.spring.service;  
  2. /** 
  3.  * 介面A 
  4.  */
  5. publicinterface AService {  
  6.     publicvoid fooA(String _msg);  
  7.     publicvoid barA();  
  8. }  
  Java程式碼  收藏程式碼
  1. package com.spring.service;  
  2. /** 
  3.  *介面A的實現類 
  4.  */
  5. publicclass AServiceImpl implements AService {  
  6.     publicvoid barA() {  
  7.         System.out.println("AServiceImpl.barA()");  
  8.     }  
  9.     publicvoid fooA(String _msg) {  
  10.         System.out.println("AServiceImpl.fooA(msg:"+_msg+")");  
  11.     }  
  12. }  
Java程式碼  收藏程式碼
  1. package com.spring.service;  
  2. /** 
  3.  *   Service類B 
  4.  */
  5. publicclass BServiceImpl {  
  6.     publicvoid barB(String _msg, int _type) {  
  7.         System.out.println("BServiceImpl.barB(msg:"+_msg+" type:"+_type+")");  
  8.         if(_type == 1)  
  9.             thrownew IllegalArgumentException("測試異常");  
  10.     }  
  11.     publicvoid fooB() {  
  12.         System.out.println("BServiceImpl.fooB()");  
  13.     }  
  14. }  
 

    ApplicationContext

Java程式碼  收藏程式碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.     xmlns:aop="http://www.springframework.org/schema/aop"
  5.     xsi:schemaLocation="  
  6.             http://www.springframework.org/schema/beans
  7.             http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  8.             http://www.springframework.org/schema/aop
  9.             http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
  10.     default-autowire="autodetect">  
  11.     <aop:config>  
  12.         <aop:aspect id="TestAspect" ref="aspectBean">  
  13.             <!--配置com.spring.service包下所有類或介面的所有方法-->  
  14.             <aop:pointcut id="businessService"
  15.                 expression="execution(* com.spring.service.*.*(..))" />  
  16.             <aop:before pointcut-ref="businessService" method="doBefore"/>  
  17.             <aop:after pointcut-ref="businessService" method="doAfter"/>  
  18.             <aop:around pointcut-ref="businessService" method="doAround"/>  
  19.             <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>  
  20.         </aop:aspect>  
  21.     </aop:config>  
  22.     <bean id="aspectBean"class="com.spring.aop.TestAspect" />  
  23.     <bean id="aService"class="com.spring.service.AServiceImpl"></bean>  
  24.     <bean id="bService"class="com.spring.service.BServiceImpl"></bean>  
  25. </beans>  

    測試類AOPTest

Java程式碼  收藏程式碼
  1. publicclass AOPTest extends AbstractDependencyInjectionSpringContextTests {  
  2.     private AService aService;  
  3.     private BServiceImpl bService;  
  4.     protected String[] getConfigLocations() {  
  5.         String[] configs = new String[] { "/applicationContext.xml"};  
  6.         return configs;  
  7.     }  
  8.     /** 
  9.      * 測試正常呼叫 
  10.      */
  11.     publicvoid testCall()  
  12.     {  
  13.         System.out.println("SpringTest JUnit test");  
  14.         aService.fooA("JUnit test fooA");  
  15.         aService.barA();  
  16.         bService.fooB();  
  17.         bService.barB("JUnit test barB",0);  
  18.     }  
  19.     /** 
  20.      * 測試After-Throwing 
  21.      */
  22.     publicvoid testThrow()  
  23.     {  
  24.         try {  
  25.             bService.barB("JUnit call barB",1);  
  26.         } catch (IllegalArgumentException e) {  
  27.         }  
  28.     }  
  29.     publicvoid setAService(AService service) {  
  30.         aService = service;  
  31.     }  
  32.     publicvoid setBService(BServiceImpl service) {  
  33.         bService = service;  
  34.     }  
  35. }  

    執行結果如下:

Java程式碼  收藏程式碼
  1. log Begining method: com.spring.service.AServiceImpl.fooA  
  2. AServiceImpl.fooA(msg:JUnit test fooA)  
  3. log Ending method: com.spring.service.AServiceImpl.fooA  
  4. process time: 0 ms  
  5. log Begining method: com.spring.service.AServiceImpl.barA  
  6. AServiceImpl.barA()  
  7. log Ending method: com.spring.service.AServiceImpl.barA  
  8. process time: 0 ms  
  9. log Begining method: com.spring.service.BServiceImpl.fooB  
  10. BServiceImpl.fooB()  
  11. log Ending method: com.spring.service.BServiceImpl.fooB  
  12. process time: 0 ms  
  13. log Begining method: com.spring.service.BServiceImpl.barB  
  14. BServiceImpl.barB(msg:JUnit test barB type:0)  
  15. log Ending method: com.spring.service.BServiceImpl.barB  
  16. process time: 0 ms  
  17. log Begining method: com.spring.service.BServiceImpl.barB  
  18. BServiceImpl.barB(msg:JUnit call barB type:1)  
  19. log Ending method: com.spring.service.BServiceImpl.barB  
  20. method com.spring.service.BServiceImpl.barB throw exception  
  21. 測試異常  
 

    《Spring參考手冊》中定義了以下幾個AOP的重要概念,結合以上程式碼分析如下:

  • 切面(Aspect):官方的抽象定義為“一個關注點的模組化,這個關注點可能會橫切多個物件”,在本例中,“切面”就是類TestAspect所關注的具體行為,例如,AServiceImpl.barA()的呼叫就是切面TestAspect所關注的行為之一。“切面”在ApplicationContext中<aop:aspect>來配置。
  • 連線點(Joinpoint):程式執行過程中的某一行為,例如,AServiceImpl.barA()的呼叫或者BServiceImpl.barB(String _msg, int _type)丟擲異常等行為。
  • 通知(Advice):“切面”對於某個“連線點”所產生的動作,例如,TestAspect中對com.spring.service包下所有類的方法進行日誌記錄的動作就是一個Advice。其中,一個“切面”可以包含多個“Advice”,例如TestAspect
  • 切入點(Pointcut):匹配連線點的斷言,在AOP中通知和一個切入點表示式關聯。例如,TestAspect中的所有通知所關注的連線點,都由切入點表示式execution(* com.spring.service.*.*(..))來決定
  • 目標物件(Target Object):被一個或者多個切面所通知的物件。例如,AServcieImpl和BServiceImpl,當然在實際執行時,Spring AOP採用代理實現,實際AOP操作的是TargetObject的代理物件。
  • AOP代理(AOP Proxy)在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。預設情況下,TargetObject實現了介面時,則採用JDK動態代理,例如,AServiceImpl;反之,採用CGLIB代理,例如,BServiceImpl。強制使用CGLIB代理需要將 <aop:config>proxy-target-class 屬性設為true

       通知(Advice)型別

  • 前置通知(Before advice):在某連線點(JoinPoint)之前執行的通知,但這個通知不能阻止連線點前的執行。ApplicationContext中在<aop:aspect>裡面使用<aop:before>元素進行宣告。例如,TestAspect中的doBefore方法
  • 後通知(After advice):當某連線點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裡面使用<aop:after>元素進行宣告。例如,TestAspect中的doAfter方法,所以AOPTest中呼叫BServiceImpl.barB丟擲異常時,doAfter方法仍然執行
  • 返回後通知(After return advice):在某連線點正常完成後執行的通知,不包括丟擲異常的情況。ApplicationContext中在<aop:aspect>裡面使用<after-returning>元素進行宣告。
  • 環繞通知(Around advice):包圍一個連線點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的呼叫前後完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行宣告。例如,TestAspect中的doAround方法。
  • 丟擲異常後通知(After throwing advice): 在方法丟擲異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裡面使用<aop:after-throwing>元素進行宣告。例如,TestAspect中的doThrowing方法。

       切入點表示式

  • 通常情況下,表示式中使用”execution“就可以滿足大部分的要求。表示式格式如下:
Java程式碼  收藏程式碼
  1. execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)  

modifiers-pattern:方法的操作許可權

ret-type-pattern:返回值

declaring-type-pattern:方法所在的包

name-pattern:方法名

parm-pattern:引數名

throws-pattern:異常

其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值為任意型別;方法名任意;引數不作限制的所有方法。

  • 通知引數

可以通過args來繫結引數,這樣就可以在通知(Advice)中訪問具體引數了。例如,<aop:aspect>配置如下

Java程式碼  收藏程式碼
  1. <aop:config>  
  2.     <aop:aspect id="TestAspect" ref="aspectBean">  
  3.         <aop:pointcut id="businessService"
  4.             expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />  
  5.             <aop:after pointcut-ref="businessService" method="doAfter"/>  
  6.     </aop:aspect>  
  7. </aop:config>  

TestAspect的doAfter方法中就可以訪問msg引數,但這樣以來AService中的barA()和BServiceImpl中的barB()就不再是連線點,因為execution(* com.spring.service.*.*(String,..))只配置第一個引數為String型別的方法。其中,doAfter方法定義如下:

Java程式碼  收藏程式碼
  1. publicvoid doAfter(JoinPoint jp,String msg)  
  •   訪問當前的連線點

任何通知(Advice)方法可以將第一個引數定義為 org.aspectj.lang.JoinPoint 型別。JoinPoint 介面提供了一系列有用的方法, 比如 getArgs()(返回方法引數)、getThis()(返回代理物件)、getTarget()(返回目標)、getSignature()(返回正在被通知的方法相關資訊)和 toString()(打印出正在被通知的方法的有用資訊。

相關推薦

springBoot AOP切面程式設計

AOP 為 Aspect Oriented Programming 的縮寫,意為 面向切面程式設計。AOP 為spring 中的一個重要內容,它是通過對既有程式定義一個切入點,然後在其前後切入不同的執行內容。 AOP 

AOP切面程式設計(動態代理)

面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。 怎麼算是面向切面?在不改變原始碼的情況下嵌入一塊其他的程式碼塊兒。 水平有限也解釋不太好。還是舉例說明。 原來我們有一個介面 public interface StudentInfoService

spring 非xml配置 非@Aspect 註解 使用aop切面程式設計 方便深入學習aop原始碼

使用maven專案,實現基於純java的類程式碼實現spring的aop功能,不用xml配置,不用aop註解實現aop,直接使用類的java程式碼實現,直接執行main方法,看到aop結果輸出 //pom.xml <?xml version="1.0" encoding="UT

SpringBoot 通過自定義註解實現AOP切面程式設計例項

一直心心念的想寫一篇關於AOP切面例項的博文,拖更了許久之後,今天終於著手下筆將其完成。 基礎概念 1、切面(Aspect) 首先要理解‘切’字,需要把物件想象成一個立方體,傳統的面向物件變成思維,類定義完成之後(封裝)。每次例項化一個物件,對類定義中的成員變數賦值,就相當於對這個立方體進行了一個定義,

Spring中基於AspectJ的AOP切面程式設計介紹及實現

簡介: AOP Aspect Oriented Programing 面向切面程式設計 AOP採取==橫向抽取==機制,取代了傳統==縱向繼承==體系重複性程式碼(效能監視、事務管理、安全檢查、快取) Spring中的Aop是純Java來實現的,使用==動態代理==的方式增強程

Spring AOP 後篇(三): AOP切面程式設計

Spring AOP 後篇: AOP切面程式設計 該文章參考多篇文章的基礎上進行了簡化並做少許修改,方便理解。原文章地址如下: Spring3:AOP Spring的AOP面向切面程式設計 一、理解切入點表示式(execution())(

SpringBoot學習(四)—AOP切面程式設計

在開始之前先來一篇aop的介紹http://blog.csdn.net/Intlgj/article/details/5671248  aop(面向切面程式設計)不同於oop(面向物件程式設計),ao

SpringBoot2之AOP切面程式設計

Why AOP? Aspect Oriented Programming(AOP),面向切面程式設計,是一個比較熱門的話題。AOP主要實現的目的是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。比如我們最常見的就

spring AOP切面程式設計——基於自定義註解

AOP稱為面向切面程式設計,在程式開發中主要用來解決一些系統層面上的問題,比如日誌,事務,許可權等待, (1)Aspect(切面):通常是一個類,裡面可以定義切入點和通知 (2)JointPoint(連線點):程式執行過程中明確的點,一般是方法的呼叫 (3)Advice(

AOP 切面程式設計

此前對於AOP的使用僅限於宣告式事務,除此之外在實際開發中也沒有遇到過與之相關的問題。最近專案中遇到了以下幾點需求,仔細思考之後,覺得采用AOP來解決。一方面是為了以更加靈活的方式來解決問題,另一方面是藉此機會深入學習SpringAOP相關的內容。本文是權當本人的自己AOP

spring---Aop切面程式設計

一、學習aop之前先了解一些Aop的相關術語: 1、通知(Advice):定義切面是什麼以及何時使用。描述了切面要完成的工作以及何時需要執行這個工作。 2、連線點(JoinPoint):程式能夠應用通知的一個時機,這些時機就是連線點,如方法被呼叫時、異常被丟擲時等 3、

許可權管理系統 AOP切面程式設計控制權限之切面

@Component @Aspect public class PermissionAspect {@Resourceprivate UserService userservice;@Resourceprivate RoleService roleservice;@Reso

Spring-AOP切面程式設計總結

寫在前面:   之前寫了三篇JAVA基礎進階、一篇JAVA原始碼解析,今天又過來寫框架,大家別擔心,另外兩個以後還會繼續寫的,給大家預告下,下一篇部落格會寫JAVA基礎進階的《JAVA反射機制》,然後會寫JAVA原始碼解析的集合原始碼解析,那塊應該一兩個集合型

[Spring]AOP切面程式設計/原理/基於註解/基於xml

AOP概念不講廢話,面向切面就比如說有很多個業務邏輯程式碼,如果你要修改程式碼,在程式碼實現前後增加一條邏輯,比如要判斷後才執行程式碼,你總不能一條條去改各個類的程式碼。所以切面就是說執行一個方法,這個方法變為一個切入點來配置,你可以定義這個切入點,你想在執行這個方法之前增加

AOP切面程式設計(埋點選單統計)

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public final void org.mybatis.spring.support.SqlSessionDaoSu

【小家java】POP(面向過程程式設計)、OOP(面向物件程式設計)、AOP(面向切面程式設計)三種程式設計思想的區別和聯絡

相關閱讀 【小家java】java5新特性(簡述十大新特性) 重要一躍 【小家java】java6新特性(簡述十大新特性) 雞肋升級 【小家java】java7新特性(簡述八大新特性) 不溫不火 【小家java】java8新特性(簡述十大新特性) 飽受讚譽 【小家java】java9

3.AOP面向切面程式設計

1. 首先我要控制事務,要想控制事務就得使用一個connection 所以只能提取出來connection 所以注重物件的注入 這個重點就是怎麼注入的問題? 重點:加強聯絡 IOC 2. 1.怎麼踢掉重複程式碼? 2.動態代理!AOP 作用就是在

Spring Boot實戰系列(3)AOP面向切面程式設計

AOP是一種與語言無關的程式思想、程式設計正規化。專案業務邏輯中,將通用的模組以水平切割的方式進行分離統一處理,常用於日誌、許可權控制、異常處理等業務中。 快速導航 引入AOP依賴 AOP常用註解解析 實現日誌分割功能 @Pointcut 新增切入點 @Be

02 Spring的AOP(面向切面程式設計

1、關於AOP AOP(Aspect Oriented Programming),即面向切面程式設計,可以說是OOP(Object Oriented Programming,面向物件程式設計)的補充和完善。OOP引入封裝、繼承、多型等概念來建立一種物件層次結構,用於模擬公共行為的一個集合。O

第二天 : AOP 面向切面程式設計 、 JdbcTemplete 工具類使用

AOP 面向切面程式設計 、 JdbcTemplete 工具類使用 目錄 一、 什麼是 AOP ***** 二、 AOP 的底層實現 1、 JDK動態代理 2、 使用CGlib 完成動態代理 三、 Spring AOP 1、 傳統Spring AOP 提供