1. 程式人生 > >10、SSM框架-Spring AOP之基於註解的宣告式AspectJ(10)

10、SSM框架-Spring AOP之基於註解的宣告式AspectJ(10)

spring除了支援Schema方式配置AOP,還支援註解方式:使用@AspectJ風格的切面宣告。匯入需要的包:aspectjweaver.jar、aopalliance-1.0.jar

一、基本使用方法

 1.1、啟用對@AspectJ的支援

       Spring預設不支援@AspectJ風格的切面宣告,為了支援需要使用如下配置:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <!-- 啟動@AspectJ支援 -->  
  2. <!-- proxy-target-class預設"false",更改為"ture"使用CGLib動態代理 -->    
  3. <aop:aspectj-autoproxy proxy-target-class="false"/>
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <!-- 啟動@AspectJ支援 -->  
  2. <!-- proxy-target-class預設"false",更改為"ture"使用CGLib動態代理 -->    
  3. <aop:aspectj-autoproxy proxy-target-class="false"/>  
	<!-- 啟動@AspectJ支援 -->
	<!-- proxy-target-class預設"false",更改為"ture"使用CGLib動態代理 -->  
	<aop:aspectj-autoproxy proxy-target-class="false"/>
這樣Spring就能發現@AspectJ風格的切面並且將切面應用到目標物件。

 1.2、 宣告切面

       @AspectJ風格的宣告切面非常簡單,使用@Aspect註解進行宣告:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Aspect  
  2. ublic class AdivceMethod {
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Aspect  
  2. ublic class AdivceMethod {  
 @Aspect
public class AdivceMethod {
然後將該切面在配置檔案中宣告為Bean後,Spring就能自動識別並進行AOP方面的配置: [html]
view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <bean id="aspect" class="……AdivceMethod"/>
[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. <bean id="aspect" class="……AdivceMethod"/>  
<bean id="aspect" class="……AdivceMethod"/>
或者直接使用元註解的方法: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Component  
  2. @Aspect  
  3. public class AdivceMethod {

 1.3、 宣告切入點

       @AspectJ風格的命名切入點使用org.aspectj.lang.annotation包下的@Pointcut+方法(方法必須是返回void型別)實現。

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Pointcut(value="切入點表示式", argNames = "引數名列表")  
  2. public void pointcutName(……) {}
       value:指定切入點表示式;

       argNames:指定命名切入點方法引數列表引數名字,可以有多個用“,”分隔,這些引數將傳遞給通知方法同名的引數,同時比如切入點表示式“args(param)”將匹配引數型別為命名切入點方法同名引數指定的引數型別。

       pointcutName:切入點名字,可以使用該名字進行引用該切入點表示式。

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")    
  2. public void beforePointcut(String param) {} 
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")    
  2. public void beforePointcut(String param) {}    
    @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)", argNames = "param")  
    public void beforePointcut(String param) {}  
定義了一個切入點,名字為“beforePointcut”,該切入點將匹配目標方法的第一個引數型別為通知方法實現中引數名為“param”的引數型別。

 二、宣告通知

       @AspectJ風格的宣告通知也支援5種通知型別:

2.1、前置通知:使用org.aspectj.lang.annotation 包下的@Before註解宣告;

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Before(value = "切入點表示式或命名切入點", argNames = "引數列表引數名"
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Before(value = "切入點表示式或命名切入點", argNames = "引數列表引數名")    
    @Before(value = "切入點表示式或命名切入點", argNames = "引數列表引數名")  
      value:指定切入點表示式或命名切入點;

       argNames:與Schema方式配置中的同義。

接下來示例一下吧:

1、定義介面和實現,在此我們就使用Schema風格時的定義;

2、定義切面:

3、定義切入點:

4、定義通知:

2.2、後置返回通知:使用org.aspectj.lang.annotation 包下的@AfterReturning註解宣告;

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @AfterReturning(    
  2. value="切入點表示式或命名切入點",    
  3. pointcut="切入點表示式或命名切入點",    
  4. argNames="引數列表引數名",    
  5. returning="返回值對應引數名")
[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @AfterReturning(    
  2. value="切入點表示式或命名切入點",    
  3. pointcut="切入點表示式或命名切入點",    
  4. argNames="引數列表引數名",    
  5. returning="返回值對應引數名")    
    @AfterReturning(  
    value="切入點表示式或命名切入點",  
    pointcut="切入點表示式或命名切入點",  
    argNames="引數列表引數名",  
    returning="返回值對應引數名")  

       value:指定切入點表示式或命名切入點;

       pointcut:同樣是指定切入點表示式或命名切入點,如果指定了將覆蓋value屬性指定的,pointcut具有高優先順序;

       argNames:與Schema方式配置中的同義;

       returning:與Schema方式配置中的同義。

2.3、後置異常通知:使用org.aspectj.lang.annotation 包下的@AfterThrowing註解宣告;

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @AfterThrowing (    
  2. value="切入點表示式或命名切入點",    
  3. pointcut="切入點表示式或命名切入點",    
  4. argNames="引數列表引數名",    
  5. throwing="異常對應引數名")
[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @AfterThrowing (    
  2. value="切入點表示式或命名切入點",    
  3. pointcut="切入點表示式或命名切入點",    
  4. argNames="引數列表引數名",    
  5. throwing="異常對應引數名")    
@AfterThrowing (  
value="切入點表示式或命名切入點",  
pointcut="切入點表示式或命名切入點",  
argNames="引數列表引數名",  
throwing="異常對應引數名")  

       value:指定切入點表示式或命名切入點;

       pointcut:同樣是指定切入點表示式或命名切入點,如果指定了將覆蓋value屬性指定的,pointcut具有高優先順序;

       argNames:與Schema方式配置中的同義;

       throwing:與Schema方式配置中的同義。

其中測試程式碼與Schema方式幾乎一樣,在此就不演示了,如果需要請參考AopTest.Java中的testAnnotationAfterThrowingAdvice測試方法。

2.4、後置最終通知:使用org.aspectj.lang.annotation 包下的@After註解宣告;

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @After (    
  2. value="切入點表示式或命名切入點",    
  3. argNames="引數列表引數名")

       value:指定切入點表示式或命名切入點;

       argNames:與Schema方式配置中的同義;

2.5、環繞通知:使用org.aspectj.lang.annotation 包下的@Around註解宣告;

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Around (    
  2. value="切入點表示式或命名切入點",    
  3. argNames="引數列表引數名")

       value:指定切入點表示式或命名切入點;

       argNames:與Schema方式配置中的同義;

2.6  引入

       @AspectJ風格的引入宣告在切面中使用org.aspectj.lang.annotation包下的@DeclareParents宣告:

[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @DeclareParents(    
  2. value=" AspectJ語法型別表示式",    
  3. defaultImpl=引入介面的預設實現類)    
  4. private Interface interface;
[html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @DeclareParents(    
  2. value=" AspectJ語法型別表示式",    
  3. defaultImpl=引入介面的預設實現類)    
  4. private Interface interface;    
    @DeclareParents(  
    value=" AspectJ語法型別表示式",  
    defaultImpl=引入介面的預設實現類)  
    private Interface interface;  

       value:匹配需要引入介面的目標物件的AspectJ語法型別表示式;與Schema方式中的types-matching屬性同義;

       private Interface interface指定需要引入的介面;

       defaultImpl指定引入介面的預設實現類,沒有與Schema方式中的delegate-ref屬性同義的定義方式;


三、使用範例

整個工程目錄如下:


記得匯入包:aopalliance-1.0.jar+aspectjweaver.jar+commons-logging-1.2.jar+spring

1、首先新建一個人的介面類:

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package com.mucfu.aspectj;  
  2. /**   
  3. *功能  人的介面類 
  4. *作者 林炳文([email protected] 部落格:http://blog.csdn.net/evankaka)   
  5. *時間 2015.4.27 
  6. */  
  7. public interface Person {  
  8.     public void eatBreakfast();  
  9.     public void eatLunch();  
  10.     public void eatSupper();  
  11.     public String drink(String name);  
  12. }
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package com.mucfu.aspectj;  
  2. /**   
  3. *功能  人的介面類 
  4. *作者 林炳文([email protected] 部落格:http://blog.csdn.net/evankaka)   
  5. *時間 2015.4.27 
  6. */  
  7. public interface Person {  
  8.     public void eatBreakfast();  
  9.     public void eatLunch();  
  10.     public void eatSupper();  
  11.     public String drink(String name);  
  12. }  
package com.mucfu.aspectj;
/**  
*功能  人的介面類
*作者 林炳文([email protected] 部落格:http://blog.csdn.net/evankaka)  
*時間 2015.4.27
*/
public interface Person {
	public void eatBreakfast();
	public void eatLunch();
	public void eatSupper();
	public String drink(String name);
	

}
2、來個baby的實現類: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package com.mucfu.aspectj;  
  2. import org.springframework.stereotype.Component;  
  3. /**   
  4. *功能  人的實現類 
  5. *作者 林炳文([email protected] 部落格:http://blog.csdn.net/evankaka)   
  6. *時間 2015.4.27 
  7. */  
  8. @Component  
  9. public class BabyPerson implements Person{  
  10.     @Override  
  11.     public void eatBreakfast() {  
  12.         System.out.println("小Baby正在吃早餐");  
  13.     }  
  14.     @Override  
  15.     public void eatLunch() {  
  16.         System.out.println("小Baby正在吃午餐");  
  17.     }  
  18.     @Override  
  19.     public void eatSupper() {  
  20.         System.out.println("小Baby正在吃晚餐");  
  21.     }  
  22.     @Override  
  23.     public String drink(String name) {  
  24.         return "小Baby在喝:"+name;  
  25.     }  
  26. }
3、然後後就是對Bayby吃飯的函式進行各種增強 [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package com.mucfu.aspectj;  
  2. import org.aspectj.lang.ProceedingJoinPoint;  
  3. import org.aspectj.lang.annotation.After;  
  4. import org.aspectj.lang.annotation.AfterReturning;  
  5. import org.aspectj.lang.annotation.Around;  
  6. import org.aspectj.lang.annotation.Aspect;  
  7. import org.aspectj.lang.annotation.Before;  
  8. import org.springframework.stereotype.Component;  
  9. @Component  
  10. @Aspect  
  11. public class AdivceMethod {  
  12.     @Before("execution(* com.mucfu.aspectj.BabyPerson.*(..))")  
  13.     // 匹配BabyPerson類所有的方法,注意*和com之間有個空格  
  14.     public void beforeEat() {  
  15.         System.out  
  16.                 .println("-------------------這裡是前置增強,吃飯之前先洗小手!--------------------");  
  17.     }  
  18.     @After("execution(* eatLunch(..))")  
  19.     // 匹配該工程下所有的eatLunch方法  
  20.     public void afterEat() {  
  21.         System.out  
  22.                 .println("-------------------這裡是後置增強,午飯吃完要睡午覺!--------------------");  
  23.     }  
  24.     @Around("execution(* com.mucfu.aspectj.BabyPerson.eatSupper())")  
  25.     // 匹配該工程下BabyPerson的eatLunch方法  
  26.     public Object aroundEat(ProceedingJoinPoint pjp) throws Throwable {  
  27.         System.out  
  28.                 .println("-------------------這裡是環繞增強,吃晚飯前先玩一玩!-------------------");  
  29.         Object retVal = pjp.proceed();  
  30.         System.out  
  31.                 .println("-------------------這裡是環繞增強,晚飯吃完後要得睡覺了!-------------------");  
  32.         return retVal;  
  33.     }  
  34.     @AfterReturning(returning="rvt",pointcut="execution(* com.mucfu.aspectj.BabyPerson.drink(..))")  
  35.     public void log(Object rvt) {  
  36.         System.out  
  37.         .println("-------------------這裡是AfterReturning增強-------------------");  
  38.         System.out.println("獲取小Baby正在喝的飲料"+rvt);  
  39.         System.out.println("記錄每天喝的飲料容量");  
  40.     }  
  41. }
[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package com.mucfu.aspectj;  
  2. import org.aspectj.lang.ProceedingJoinPoint;  
  3. import org.aspectj.lang.annotation.After;  
  4. import org.aspectj.lang.annotation.AfterReturning;  
  5. import org.aspectj.lang.annotation.Around;  
  6. import org.aspectj.lang.annotation.Aspect;  
  7. import org.aspectj.lang.annotation.Before;  
  8. import org.springframework.stereotype.Component;  
  9. @Component  
  10. @Aspect  
  11. public class AdivceMethod {  
  12.     @Before("execution(* com.mucfu.aspectj.BabyPerson.*(..))")  
  13.     // 匹配BabyPerson類所有的方法,注意*和com之間有個空格  
  14.     public void beforeEat() {  
  15.         System.out  
  16.                 .println("-------------------這裡是前置增強,吃飯之前先洗小手!--------------------");  
  17.     }  
  18.     @After("execution(* eatLunch(..))")  
  19.     // 匹配該工程下所有的eatLunch方法  
  20.     public void afterEat() {  
  21.         System.out  
  22.                 .println("-------------------這裡是後置增強,午飯吃完要睡午覺!--------------------");  
  23.     }  
  24.     @Around("execution(* com.mucfu.aspectj.BabyPerson.eatSupper())")  
  25.     // 匹配該工程下BabyPerson的eatLunch方法  
  26.     public Object aroundEat(ProceedingJoinPoint pjp) throws Throwable {  
  27.         System.out  
  28.                 .println("-------------------這裡是環繞增強,吃晚飯前先玩一玩!-------------------");  
  29.         Object retVal = pjp.proceed();  
  30.         System.out  
  31.                 .println("-------------------這裡是環繞增強,晚飯吃完後要得睡覺了!-------------------");  
  32.         return retVal;  
  33.     }  
  34.     @AfterReturning(returning="rvt",pointcut="execution(* com.mucfu.aspectj.BabyPerson.drink(..))")  
  35.     public void log(Object rvt) {  
  36.         System.out  
  37.         .println("-------------------這裡是AfterReturning增強-------------------");  
  38.         System.out.println("獲取小Baby正在喝的飲料"+rvt);  
  39.         System.out.println("記錄每天喝的飲料容量");  
  40.     }  
  41. }  
package com.mucfu.aspectj;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AdivceMethod {

	@Before("execution(* com.mucfu.aspectj.BabyPerson.*(..))")
	// 匹配BabyPerson類所有的方法,注意*和com之間有個空格
	public void beforeEat() {
		System.out
				.println("-------------------這裡是前置增強,吃飯之前先洗小手!--------------------");
	}

	@After("execution(* eatLunch(..))")
	// 匹配該工程下所有的eatLunch方法
	public void afterEat() {
		System.out
				.println("-------------------這裡是後置增強,午飯吃完要睡午覺!--------------------");
	}

	@Around("execution(* com.mucfu.aspectj.BabyPerson.eatSupper())")
	// 匹配該工程下BabyPerson的eatLunch方法
	public Object aroundEat(ProceedingJoinPoint pjp) throws Throwable {
		System.out
				.println("-------------------這裡是環繞增強,吃晚飯前先玩一玩!-------------------");
		Object retVal = pjp.proceed();
		System.out
				.println("-------------------這裡是環繞增強,晚飯吃完後要得睡覺了!-------------------");
		return retVal;
	}
    @AfterReturning(returning="rvt",pointcut="execution(* com.mucfu.aspectj.BabyPerson.drink(..))")
	public void log(Object rvt) {
    	System.out
		.println("-------------------這裡是AfterReturning增強-------------------");
    	System.out.println("獲取小Baby正在喝的飲料"+rvt);
    	System.out.println("記錄每天喝的飲料容量");

	}

}
4、新建一個beans.xml,內容如下: [html] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片