spring11----基於Schema的AOP
基於Schema的AOP從Spring2.0之後通過“aop”名稱空間來定義切面、切入點及宣告通知。
在Spring配置檔案中,所以AOP相關定義必須放在<aop:config>標籤下,該標籤下可以有<aop:pointcut>、 <aop:advisor>、<aop:aspect>標籤,配置順序不可變。
一. 宣告切面
切面就是包含切入點和通知的物件,在Spring容器中將被定義為一個Bean,Schema方式的切面需要一個切面支援 Bean,該支援Bean的欄位和方法提供了切面的狀態和行為資訊,並通過配置方式來指定切入點和通知實現。
<bean id="aspect" class="com.test.spring.aop.HelloWorldAspect" /> <aop:config> <aop:pointcut id="pointcut" expression="execution(* com.test.spring.service.impl..*.*(..))" /> <aop:aspect ref="aspect"> <aop:before pointcut-ref="pointcut" method="beforeAdvice" /> <!-- <aop:after pointcut="execution(* com.test.spring.service.impl..*.*(..))" method="afterFinallyAdvice"/> --> <aop:after pointcut-ref="pointcut" method="afterFinallyAdvice" /> </aop:aspect> </aop:config>
切面使用<aop:aspect>標籤指定,ref屬性用來引用切面支援Bean。
切面支援Bean“aspectSupportBean”跟普通Bean完全一樣使用,切面使用“ref”屬性引用它。
二. 宣告切入點
切入點在Spring中也是一個Bean,Bean定義方式可以有以下3種:
1. 在<aop:config>標籤下使用<aop:pointcut>宣告一個切入點Bean,該切入點可以被多個切面使用,對於需要 共享使用的切入點最好使用該方式,該切入點使用id屬性指定Bean名字,在通知定義時使用pointcut-ref屬性通過該id 引用切入點,expression屬性指定切入點表示式:
<aop:config> <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/> <aop:aspect ref="aspectSupportBean"> <aop:before pointcut-ref="pointcut" method="before"/> </aop:aspect> </aop:config>
2. 在<aop:aspect>標籤下使用<aop:pointcut>宣告一個切入點Bean,該切入點可以被多個切面使用,但一般該 切入點只被該切面使用,當然也可以被其他切面使用,但最好不要那樣使用,該切入點使用id屬性指定Bean名字,在通 知定義時使用pointcut-ref屬性通過該id引用切入點,expression屬性指定切入點表示式:
<aop:config> <aop:aspect ref="aspectSupportBean"> <aop:pointcut id=" pointcut" expression="execution(* cn.javass..*.*(..))"/> <aop:before pointcut-ref="pointcut" method="before"/> </aop:aspect> </aop:config>
3. 匿名切入點Bean,可以在宣告通知時通過pointcut屬性指定切入點表示式,該切入點是匿名切入點,只被該通知 使用:
<aop:config> <aop:aspect ref="aspectSupportBean"> <aop:after pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/> </aop:aspect> </aop:config>
三. 宣告通知
基於Schema方式支援前邊介紹的5種通知型別:
1. 前置通知
在切入點選擇的方法之前執行,通過<aop:aspect>標籤下的<aop:before>標籤宣告:
<aop:before pointcut="切入點表示式" pointcut-ref="切入點Bean引用" method="前置通知實現方法名" arg-names="前置通知實現方法引數列表引數名字"/>
pointcut和pointcut-ref:二者選一,指定切入點;
method:指定前置通知實現方法名,如果是多型需要加上引數型別,多個用“,”隔開,如 beforeAdvice(java.lang.String);
arg-names:指定通知實現方法的引數名字,多個用“,”分隔,可選,類似於構造器注入中的引數 名注入限制:在class檔案中沒生成變數除錯資訊是獲取不到方法引數名字的,因此只有在類沒生成變數除錯資訊時才需 要使用arg-names屬性來指定引數名,如arg-names="param"表示通知實現方法的引數列表的第一個引數名字為 “param”。
舉例:
1 package com.test.spring.service; 2 3 public interface IHelloWorldService { 4 public void sayHello(String param); 5 }View Code
1 package com.test.spring.service.impl; 2 3 import com.test.spring.service.IHelloWorldService; 4 5 public class HelloWorldService implements IHelloWorldService{ 6 7 public void sayHello(String param) { 8 System.out.println("======Hello "+param); 9 } 10 }View Code
1 package com.test.spring.aop; 2 3 public class HelloWorldAspect { 4 //前置通知 5 public void beforeAdvice(String param) { 6 System.out.println("=====before advice param "+param); 7 } 8 }View Code
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="helloWorldService" class="com.test.spring.service.impl.HelloWorldService" /> <bean id="aspect" class="com.test.spring.aop.HelloWorldAspect" /> <aop:config> <aop:aspect ref="aspect"> <aop:before pointcut="execution(* com.test.spring.service.impl..*.sayHello(..)) and args(param)" //這裡的parm指的是通知種的引數 method="beforeAdvice(java.lang.String)" arg-names="param"/> //這裡的param指的是通知的param </aop:aspect> </aop:config> </beans>
1 public class AopTest { 2 @Test 3 public void test() { 4 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 5 IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService"); 6 helloWorldService.sayHello("hermioner"); 7 } 8 }View Code
說明:
1)切入點匹配:在配置中使用“execution(* cn.javass..*.sayBefore(..)) ”匹配目標方法sayHello,且使用 “args(param)”匹配目標方法只有一個引數且傳入的引數型別為通知實現方法中同名的引數型別;
2)目標方法定義:使用method=" beforeAdvice(java.lang.String) "指定前置通知實現方法,且該通知有一個引數類 型為java.lang.String引數;
3)目標方法引數命名:其中使用arg-names=" param "指定通知實現方法引數名為“param”,切入點中使用 “args(param)”匹配的目標方法引數將自動傳遞給通知實現方法同名引數。(比如:Service種的sayHello(String param),但是通知中是beforeAdvice(String param2),那麼上面的兩處arg配置都應該用param2)
2. 後置返回通知
在切入點選擇的方法正常返回時執行,通過<aop:aspect>標籤下的<aop:after-returning>標籤聲 明:
<aop:after-returning pointcut="切入點表示式" pointcut-ref="切入點Bean引用" method="後置返回通知實現方法名" arg-names="後置返回通知實現方法引數列表引數名字" returning="返回值對應的後置返回通知實現方法引數名" />
pointcut和pointcut-ref:同前置通知同義,二選一;
method:同前置通知同義;
arg-names:同前置通知同義;
returning:定義一個名字,該名字用於匹配通知實現方法的一個引數名,當目標方法執行正常返回後,將把目 標方法返回值傳給通知方法;returning限定了只有目標方法返回值匹配與通知方法相應引數型別時才能執行後置返回 通知,否則不執行,對於returning對應的通知方法引數為Object型別將匹配任何目標返回值。
舉例:
1 package com.test.spring.service; 2 3 public interface IHelloWorldService { 4 public void sayHello(String param); 5 public boolean sayAfterReturning(); 6 }View Code
1 package com.test.spring.service.impl; 2 3 import com.test.spring.service.IHelloWorldService; 4 5 public class HelloWorldService implements IHelloWorldService{ 6 7 public void sayHello(String param) { 8 System.out.println("======Hello "+param); 9 } 10 11 public boolean sayAfterReturning() { 12 System.out.println("--------after returning"); 13 return true; 14 } 15 }View Code
1 package com.test.spring.aop; 2 3 public class HelloWorldAspect { 4 //前置通知 5 public void beforeAdvice(String param2) { 6 System.out.println("=====before advice param "+param2); 7 } 8 9 //後置返回通知 10 public void afterReturningAdvice(Object retVal) { 11 System.out.println("==========after returning advice retVal:"+retVal); 12 } 13 }View Code
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 7 http://www.springframework.org/schema/aop 8 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 9 10 <bean id="helloWorldService" 11 class="com.test.spring.service.impl.HelloWorldService" /> 12 <bean id="aspect" class="com.test.spring.aop.HelloWorldAspect" /> 13 14 <aop:config> 15 <aop:aspect ref="aspect"> 16 <aop:before pointcut="execution(* com.test.spring.service.impl..*.sayHello(..)) and args(param2)" 17 method="beforeAdvice(java.lang.String)" 18 arg-names="param2"/> 19 20 <aop:after-returning pointcut="execution(* com.test.spring.service.impl..*.sayAfterReturning(..))" 21 method="afterReturningAdvice" 22 arg-names="retVal" 23 returning="retVal"/> 24 </aop:aspect> 25 </aop:config> 26 27 </beans>View Code
1 public class AopTest { 2 @Test 3 public void test() { 4 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 5 IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService"); 6 helloWorldService.sayAfterReturning(); 7 } 8 }View Code
1 --------after returning 2 ==========after returning advice retVal:trueView Code
分析:
1)切入點匹配:在配置中使用“execution(* cn.javass..*.sayAfterReturning(..)) ”匹配目標方法 sayAfterReturning,該方法返回true;
2)目標方法定義:使用method="afterReturningAdvice"指定後置返回通知實現方法;
3)目標方法引數命名:其中使用arg-names="retVal"指定通知實現方法引數名為“retVal”;
4)返回值命名:returning="retVal"用於將目標返回值賦值給通知實現方法引數名為“retVal”的引數上。
3. 後置異常通知
在切入點選擇的方法丟擲異常時執行,通過<aop:aspect>標籤下的<aop:after-throwing>標籤聲 明:
<aop:after-throwing pointcut="切入點表示式" pointcut-ref="切入點Bean引用" method="後置異常通知實現方法名" arg-names="後置異常通知實現方法引數列表引數名字" throwing="將丟擲的異常賦值給的通知實現方法引數名"/>
throwing:定義一個名字,該名字用於匹配通知實現方法的一個引數名,當目標方法丟擲異常返回後,將把目標 方法丟擲的異常傳給通知方法;throwing限定了只有目標方法丟擲的異常匹配與通知方法相應引數異常型別時才能執行 後置異常通知,否則不執行,對於throwing對應的通知方法引數為Throwable型別將匹配任何異常。
1 package com.test.spring.service; 2 3 public interface IHelloWorldService { 4 public void sayHello(String param); 5 public boolean sayAfterReturning(); 6 public void sayAfterThrowing(); 7 }View Code
1 package com.test.spring.service.impl; 2 3 import com.test.spring.service.IHelloWorldService; 4 5 public class HelloWorldService implements IHelloWorldService{ 6 7 public void sayHello(String param) { 8 System.out.println("======Hello "+param); 9 } 10 11 public boolean sayAfterReturning() { 12 System.out.println("--------after returning"); 13 return true; 14 } 15 16 public void sayAfterThrowing() { 17 System.out.println("======before thrwing"); 18 throw new RuntimeException(); 19 } 20 }View Code
1 package com.test.spring.aop; 2 3 public class HelloWorldAspect { 4 //前置通知 5 public void beforeAdvice(String param2) { 6 System.out.println("=====before advice param "+param2); 7 } 8 9 //後置返回通知 10 public void afterReturningAdvice(Object retVal) { 11 System.out.println("==========after returning advice retVal:"+retVal); 12 } 13 14 //後置異常通知 15 public void afterThrowingAdvice(Exception exception) { 16 System.out.println("======after throwing advice exception:"+exception); 17 } 18 }View Code
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 7 http://www.springframework.org/schema/aop 8 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 9 10 <bean id="helloWorldService" 11 class="com.test.spring.service.impl.HelloWorldService" /> 12 <bean id="aspect" class="com.test.spring.aop.HelloWorldAspect" /> 13 14 <aop:config> 15 <aop:aspect ref="aspect"> 16 <aop:before pointcut="execution(* com.test.spring.service.impl..*.sayHello(..)) and args(param2)" 17 method="beforeAdvice(java.lang.String)" 18 arg-names="param2"/> 19 20 <aop:after-returning pointcut="execution(* com.test.spring.service.impl..*.sayAfterReturning(..))" 21 method="afterReturningAdvice" 22 arg-names="retVal" 23 returning="retVal"/> 24 25 <aop:after-throwing pointcut="execution(* com.test.spring.service.impl..*.sayAfterThrowing(..))" 26 method="afterThrowingAdvice" 27 arg-names="exception" 28 throwing="exception"/> 29 </aop:aspect> 30 </aop:config> 31 32 </beans>View Code
1 public class AopTest { 2 @Test 3 public void test() { 4 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 5 IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService"); 6 helloWorldService.sayAfterThrowing(); 7 } 8 }View Code
1 ======before thrwing 2 ======after throwing advice exception:java.lang.RuntimeExceptionView Code
分析:
1)切入點匹配:在配置中使用“execution(* cn.javass..*.sayAfterThrowing(..))”匹配目標方法sayAfterThrowing, 該方法將丟擲RuntimeException異常;
2)目標方法定義:使用method="afterThrowingAdvice"指定後置異常通知實現方法;
3)目標方法引數命名:其中使用arg-names="exception"指定通知實現方法引數名為“exception”;
4)異常命名:returning="exception"用於將目標方法丟擲的異常賦值給通知實現方法引數名為“exception”的引數 上。
4. 後置最終通知
在切入點選擇的方法返回時執行,不管是正常返回還是丟擲異常都執行,通過<aop:aspect>標籤 下的<aop:after >標籤宣告:
<aop:after pointcut="切入點表示式" pointcut-ref="切入點Bean引用" method="後置最終通知實現方法名" arg-names="後置最終通知實現方法引數列表引數名字"/>
舉例:
1 public interface IHelloWorldService { 2 public void sayHello(String param); 3 public boolean sayAfterReturning(); 4 public void sayAfterThrowing(); 5 public boolean sayAfterFinally(); 6 }View Code
1 package com.test.spring.service.impl; 2 3 import com.test.spring.service.IHelloWorldService; 4 5 public class HelloWorldService implements IHelloWorldService{ 6 7 public void sayHello(String param) { 8 System.out.println("======Hello "+param); 9 } 10 11 public boolean sayAfterReturning() { 12 System.out.println("--------after returning"); 13 return true; 14 } 15 16 public void sayAfterThrowing() { 17 System.out.println("======before thrwing"); 18 throw new RuntimeException(); 19 } 20 21 public boolean sayAfterFinally() { 22 System.out.println("============before finally"); 23 throw new RuntimeException(); 24 } 25 }View Code
1 package com.test.spring.aop; 2 3 public class HelloWorldAspect { 4 //前置通知 5 public void beforeAdvice(String param2) { 6 System.out.println("=====before advice param "+param2); 7 } 8 9 //後置返回通知 10 public void afterReturningAdvice(Object retVal) { 11 System.out.println("==========after returning advice retVal:"+retVal); 12 } 13 14 //後置異常通知 15 public void afterThrowingAdvice(Exception exception) { 16 System.out.println("======after throwing advice exception:"+exception); 17 } 18 19 //後置最終通知 20 public void afterFinallyAdvice() { 21 System.out.println("========after finally advice"); 22 } 23 }View Code
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:aop="http://www.springframework.org/schema/aop" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 7 http://www.springframework.org/schema/aop 8 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 9 10 <bean id="helloWorldService" 11 class="com.test.spring.service.impl.HelloWorldService" /> 12 <bean id="aspect" class="com.test.spring.aop.HelloWorldAspect" /> 13 14 <aop:config> 15 <aop:aspect ref="aspect"> 16 <aop:before pointcut="execution(* com.test.spring.service.impl..*.sayHello(..)) and args(param2)" 17 method="beforeAdvice(java.lang.String)" 18 arg-names="param2"/> 19 20 <aop:after-returning pointcut="execution(* com.test.spring.service.impl..*.sayAfterReturning(..))" 21 method="afterReturningAdvice" 22 arg-names="retVal" 23 returning="retVal"/> 24 25 <aop:after-throwing pointcut="execution(* com.test.spring.service.impl..*.sayAfterThrowing(..))" 26 method="afterThrowingAdvice" 27 arg-names="exception" 28 throwing="exception"/> 29 <aop:after pointcut="execution(* com.test.spring.service.impl..*.sayAfterFinally(..))" 30 method="afterFinallyAdvice"/> 31 </aop:aspect> 32 </aop:config> 33 34 </beans>View Code
1 public class AopTest { 2 @Test 3 public void test() { 4 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 5 IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService"); 6 helloWorldService.sayAfterFinally(); 7 } 8 }View Code
1 ============before finally 2 ========after finally adviceView Code
分析:
1)切入點匹配:在配置中使用“execution(* cn.javass..*.sayAfterFinally(..))”匹配目標方法sayAfterFinally,該方法 將丟擲RuntimeException異常;
2)目標方法定義:使用method=" afterFinallyAdvice "指定後置最終通知實現方法。
5. 環繞通知
環繞著在切入點選擇的連線點處的方法所執行的通知,環繞通知非常強大,可以決定目標方法是否執 行,什麼時候執行,執行時是否需要替換方法引數,執行完畢是否需要替換返回值,可通過<aop:aspect>標籤下的 <aop:around >標籤宣告
<aop:around pointcut="切入點表示式" pointcut-ref="切入點Bean引用" method="後置最終通知實現方法名" arg-names="後置最終通知實現方法引數列表引數名字"/>
環繞通知第一個引數必須是org.aspectj.lang.ProceedingJoinPoint型別,在通知實現方法內部使用 ProceedingJoinPoint的proceed()方法使目標方法執行,proceed 方法可以傳入可選的Object[]陣列,該陣列的值將 被作為目標方法執行時的引數。
舉例:
1 package com.test.spring.service.impl; 2 3 import com.test.spring.service.IHelloWorldService; 4 5 public class HelloWorldService implements IHelloWorldService{ 6 public void sayAround(String param) { 7 System.out.println("=======around param:"+param); 8 } 9 }View Code
1 package com.test.spring.aop; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 5 public class HelloWorldAspect { 6 public Object aroundAdvice(ProceedingJoinPoint pip) throws Throwable { 7 System.out.println("=======around before"); 8 Object retVal=pip.proceed(new Object[] {"replace"}); 9 System.out.println("======around after"); 10 return retVal; 11 } 12 }View Code
1 <bean id="helloWorldService" 2 class="com.test.spring.service.impl.HelloWorldService" /> 3 <bean id="aspect" class="com.test.spring.aop.HelloWorldAspect" /> 4 5 <aop:config> 6 <aop:aspect ref="aspect"> 7 8 <aop:around pointcut="execution(* com.test.spring.service.impl..*.sayAround(..))" 9 method="aroundAdvice"/> 10 </aop:aspect> 11 </aop:config>View Code
1 public class AopTest { 2 @Test 3 public void test() { 4 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); 5 IHelloWorldService helloWorldService=(IHelloWorldService) context.getBean("helloWorldService"); 6 helloWorldService.sayAround("hahah"); 7 } 8 }View Code
1 =======around before 2 =======around param:replace 3 ======around afterView Code
分析:
1)切入點匹配:在配置中使用“execution(* cn.javass..*.sayAround(..))”匹配目標方法sayAround;
2)目標方法定義:使用method="aroundAdvice"指定環繞通知實現方法,在該實現中,第一個方法引數為pjp,型別 為ProceedingJoinPoint,其中“Object retVal = pjp.proceed(new Object[] {"replace"});”,用於執行目標方法, 且目標方法引數被“new Object[] {"replace"}”替換,最後返回“retVal ”返回值。
3)測試:我們使用“helloworldService.sayAround("haha");”傳入引數為“haha”,但最終輸出為“replace”,說 明引數被替換了。
四. Advisor
不推薦使用Advisor,除了在進行事務控制的情況下,其他情況一般不推薦使用該方式,該方式屬於侵入式設計,必 須實現通知API。
(mynote:等需要的時候再回頭學習補充)
參考文獻
https://jinnianshilongnian.iteye.com/blog/1418598