spring 4.0 AOP (使用AspectJ的註解方式 的aop實現)簡單實例

AspectJ:Java 社區裏最完整最流行的 AOP 框架. spring aop 配合使用aspectj(AOP框架)實現我們所需的aop功能

  • 在 Spring 中啓用 AspectJ 註解支持 必須在 classpath 下包含 AspectJ 類庫: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar

  • AspectJ 支持 5 種類型的通知註解:

    • @Before: 前置通知, 在方法執行之前執行
    • @After: 後置通知, 在方法執行之後執行
    • @AfterRunning: 返回通知, 在方法成功執行返回結果之後執行
    • @AfterThrowing: 異常通知, 在方法拋出異常之後
    • @Around: 環繞通知, 圍繞着方法執行
    有兩種方法實現aop功能,一個是完全基於javaconfig配置類註解實現,另一種是基於xml
  • 關於Spring AOP的AspectJ切點,最重要的一點就是Spring僅支持AspectJ切點指示器(pointcut designator)的一個子集。讓我們回顧下,Spring是基於代理的,而某些切點表達式是與基於代理的AOP無關的。表4.1列出了Spring AOP所支持的AspectJ切點指示器。

  • 基於JavaConfig配置實例 如下:

  • 目錄:


  • JavaConfig配置類:

  • [html] view plain copy
    1. package aopbean;  
    2.   
    3. import org.springframework.context.annotation.ComponentScan;  
    4. import org.springframework.context.annotation.Configuration;  
    5. import org.springframework.context.annotation.EnableAspectJAutoProxy;  
    6. @Configuration  
    7. //開啓AspectJ 自動代理模式,如果不填proxyTargetClass=true,默認爲false,  
    8. //即使用jdk默認代理模式,AspectJ代理模式是CGLIB代理模式  
    9. //如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP   
    10. //如果目標對象實現了接口,可以強制使用CGLIB實現AOP (此例子我們就是強制使用cglib實現aop)  
    11. //如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換  
    12. @EnableAspectJAutoProxy(proxyTargetClass=true)  
    13. @ComponentScan  
    14. public class JavaConfig {  
    15.   
    16. }  
    Performance接口:
  • [html] view plain copy
    1. package aopbean;  
    2.   
    3. //目標接口  
    4. public interface Performance {  
    5.       
    6.     public void perform();  
    7.   
    8. }  

    dance實現類:
  • [html] view plain copy
    1. package aopbean;  
    2.   
    3. import org.springframework.stereotype.Component;  
    4.   
    5. @Component  
    6. public class Dance implements Performance {  
    7.   
    8.     public void perform() {  
    9.         System.out.println("開始看跳舞");  
    10.   
    11.     }  
    12.   
    13. }  

    切面類Audience:
  • [html] view plain copy
    1. package aopbean;  
    2.   
    3. import org.aspectj.lang.annotation.After;  
    4. import org.aspectj.lang.annotation.Aspect;  
    5. import org.aspectj.lang.annotation.Before;  
    6. import org.aspectj.lang.annotation.Pointcut;  
    7. import org.springframework.stereotype.Component;  
    8.   
    9. @Aspect//聲明切面,沒有聲明的話不會起作用  
    10. @Component  
    11. public class Audience {  
    12.     public Audience(){  
    13.           
    14.     }  
    15.       
    16.     //聲明切入點  
    17.     //第一個*表示 方法  返回值(例如public int)  
    18.     //第二個* 表示方法的全限定名(即包名+類名)  
    19.     //perform表示目標方法參數括號兩個.表示任意類型參數  
    20.     //方法表達式以“*”號開始,表明了我們不關心方法返回值的類型。然後,我們指定了全限定類名和方法名。對於方法參數列表,  
    21.     //我們使用兩個點號(..)表明切點要選擇任意的perform()方法,無論該方法的入參是什麼  
    22.     //execution表示執行的時候觸發  
    23.     @Pointcut("execution(* *.perform(..))")  
    24.     public void dancepoint(){  
    25.         //該方法就是一個標識方法,爲pointcut提供一個依附的地方  
    26.     }  
    27.       
    28.     @Before("dancepoint()")  
    29.     public void beforeDance(){  
    30.         System.out.println("找座位。。。。");  
    31.     }  
    32.     @After("dancepoint()")  
    33.     public void afterDance(){  
    34.         System.out.println("看完回家");  
    35.     }  
    36.       
    37.   
    38. }  
    測試類:
  • [html] view plain copy
    1. package aopbean;  
    2.   
    3. import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
    4.   
    5. public class Test {  
    6.   
    7.       
    8.         public static void main(String[] args) {  
    9.             AnnotationConfigApplicationContext ac =  
    10.                     new AnnotationConfigApplicationContext(JavaConfig.class);  
    11.          Dance d = ac.getBean("dance",Dance.class);  
    12.          d.perform();  
    13.   
    14.         }  
    15.   
    16. }  

    輸出結果:

    找座位。。。。
    開始看跳舞
    看完回家
  • 環繞通知 round
  • [html] view plain copy
    1. @Around("dancepoint()")  
    2. public void roudDance(ProceedingJoinPoint jp) throws Throwable{  
    3.     System.out.println("找座位");  
    4.     jp.proceed();  
    5.     System.out.println("回家");  
    6.       
    7. }  
    輸出:找座位
    開始看跳舞
    回家
  • 可以看到,這個通知所達到的效果與之前的前置通知和後置通知是一樣的。但是,現在它們位於同一個方法中,不像之前那樣分散在四個不同的通知方法裏面。

    關於這個新的通知方法,你首先注意到的可能是它接受ProceedingJoinPoint作爲參數。這個對象是必須要有的,因爲你要在通知中通過它來調用被通知的方法。通知方法中可以做任何的事情,當要將控制權交給被通知的方法時,它需要調用ProceedingJoinPointproceed()方法。

    需要注意的是,別忘記調用proceed()方法。如果不調這個方法的話,那麼你的通知實際上會阻塞對被通知方法的調用。有可能這就是你想要的效果,但更多的情況是你希望在某個點上執行被通知的方法。

    有意思的是,你可以不調用proceed()方法,從而阻塞對被通知方法的訪問,與之類似,你也可以在通知中對它進行多次調用。要這樣做的一個場景就是實現重試邏輯,也就是在被通知方法失敗後,進行重複嘗試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章