1. 程式人生 > >spring 4.0 AOP (使用AspectJ的註解方式 的aop實現)簡單例項

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配置類:

  • package aopbean;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    @Configuration
    //開啟AspectJ 自動代理模式,如果不填proxyTargetClass=true,預設為false,
    //即使用jdk預設代理模式,AspectJ代理模式是CGLIB代理模式
    //如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP 
    //如果目標物件實現了介面,可以強制使用CGLIB實現AOP (此例子我們就是強制使用cglib實現aop)
    //如果目標物件沒有實現了介面,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
    @EnableAspectJAutoProxy(proxyTargetClass=true)
    @ComponentScan
    public class JavaConfig {
    
    }
    
    Performance介面:
  • package aopbean;
    
    //目標介面
    public interface Performance {
    	
    	public void perform();
    
    }

    dance實現類:
  • package aopbean;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Dance implements Performance {
    
    	public void perform() {
    		System.out.println("開始看跳舞");
    
    	}
    
    }

    切面類Audience:
  • package aopbean;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Aspect//宣告切面,沒有宣告的話不會起作用
    @Component
    public class Audience {
    	public Audience(){
    		
    	}
    	
    	//宣告切入點
    	//第一個*表示 方法  返回值(例如public int)
    	//第二個* 表示方法的全限定名(即包名+類名)
    	//perform表示目標方法引數括號兩個.表示任意型別引數
    	//方法表示式以“*”號開始,表明了我們不關心方法返回值的型別。然後,我們指定了全限定類名和方法名。對於方法引數列表,
    	//我們使用兩個點號(..)表明切點要選擇任意的perform()方法,無論該方法的入參是什麼
    	//execution表示執行的時候觸發
    	@Pointcut("execution(* *.perform(..))")
    	public void dancepoint(){
    		//該方法就是一個標識方法,為pointcut提供一個依附的地方
    	}
    	
    	@Before("dancepoint()")
    	public void beforeDance(){
    		System.out.println("找座位。。。。");
    	}
    	@After("dancepoint()")
    	public void afterDance(){
    		System.out.println("看完回家");
    	}
    	
    
    }
    測試類:
  • package aopbean;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Test {
    
    	
    		public static void main(String[] args) {
    			AnnotationConfigApplicationContext ac =
    					new AnnotationConfigApplicationContext(JavaConfig.class);
             Dance d = ac.getBean("dance",Dance.class);
             d.perform();
    
    		}
    
    }

    輸出結果:

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

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

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

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