Spring AOP 前切入點及多參數問題

問題:看了不少關於Spring AOP的文章,在講各類切入方式(before、around、after-returnning、thrown等)時,被切入的業務主體Bean的方法,基本都是無參數的。


也有提到有參數的,但都是一個String型的參數。

以before爲例,無參數方法的切點配置爲 java

Xml代碼   收藏代碼
  1. <aop:before method="before" pointcut="execution(* cn.xxxx..*.*(..))"/>    



若是方法有一個String型的參數param,則配置爲 spring

Xml代碼   收藏代碼
  1. <aop:before method="before" arg-names="param" pointcut="execution(* cn.xxxx..*.*(..)) and args(param)"/>    



注:若是參數其實可爲任何類型,即Object。若是非要強調是String型,參數爲其餘類型的方法,不想被切點切入,則可寫成 method="before(java.lang.String)"
(經實際測試 arg-names="param" 不寫也能夠)

但若是 cn.xxxx..*.* 的方法有多個參數,且個數不定,要想讓切點能夠切入,這麼個寫法就不行了。

我搜了N多的帖子,也沒能找到方法,最終幾經展轉,終於在網友的幫助下,點破了這一層窗戶紙,其實也很簡單,仍是在配置的寫法: 測試

Xml代碼   收藏代碼
  1. <aop:before method="before" pointcut="execution(* cn.xxxx..*.*(..)) and args(..)"/>    



與之配合的切點的寫法是 spa

Java代碼   收藏代碼
  1. public void before(JoinPoint jp) throws Throwable {  
  2. ...  
  3. }  



這樣,不論業務Bean的方法有多少個參數,均可以被這個切點切入了。若是須要訪問各個參數,只需 xml

Java代碼   收藏代碼
  1. Object[] args = jp.getArgs();  



----------------------------------華麗的分割線-----------------------------------

其實,可以知足如上需求的方法,至少還有一種,就是攔截器。
blog

Mxl代碼   收藏代碼
  1. <bean id="myService" class="org.springframework.aop.framework.ProxyFactoryBean">  
  2.     <property name="interceptorNames">  
  3.         <list>  
  4.             <value>beforeInterceptor</value>  
  5.         </list>  
  6.     </property>  
  7.     <property name="target">  
  8.         <ref bean="realServiceTarget" />  
  9.     </property>  
  10. </bean>  
  11.   
  12. <bean id="realServiceTarget" class="cn.xxxx.xxx.Xxxx"/>  
  13. <bean id="beforeInterceptor" class="cn.xxxx.xxx.MyChecker"/>  



思路是在注入ServiceBean時,偷樑換柱一下,用myService代替,實際是指向Spring的攔截器,它能夠在執行真正的ServiceBean以前,先執行beforeInterceptor所指向的攔截代碼
(這裏是MyChecker,這個攔截器要實現org.aopalliance.intercept.MethodInterceptor接口, 並完成public Object invoke(MethodInvocation invocation) throws Throwable方法)而後再交還給target屬性指明的真正的ServiceBean。它不單單可以獲得方法的參數,並且還有更強的功能——決定是 否繼續執行target。 接口

Java代碼   收藏代碼
  1. public class MyChecker implements MethodInterceptor {  
  2.   
  3.     /** 
  4.      * 用戶訪問認證方法。 
  5.      * 若是登陸合法則開始執行服務,不然返回錯誤。 
  6.      */  
  7.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  8.         Method invokeMethod = invocation.getMethod();  
  9.         Object[] args = invocation.getArguments();  
  10.         ...  
  11.                 if(....){  
  12.                     return invocation.proceed(); // 檢查OK,繼續執行  
  13.                 }else{  
  14.                     return null;  // 檢查NG,阻斷執行  
  15.                 }  
  16.         }  
  17. }  



看到了吧,這裏不單單能夠獲得目標方法的參數 Object[] args = invocation.getArguments();
還能夠,控制是否要繼續執行目標方法,仍是阻斷。所以用來作認證是再合適不過了。

----------------------------------華麗的分割線-----------------------------------

爲何要加入兩條分割線之間的之一段呢?除了也做爲before切入的另外一種實現方式,更主要的是想引出下面的問題:

我發現,在我成功的用AOP切入以後,每次都執行了兩遍切點,且我檢查了配置文件,並無重複定義<AOP>

當我在切點代碼中加入 get

Java代碼   收藏代碼
  1. Object target = jp.getTarget();  



我發現,這個target有一次是個人ServiceBean,而另外一次是相似$Proxy22之類的東西。這才恍然大悟,它應該是那個攔截器!也就是說,攔截器被切點切入了一次,真正的target又切入了一次。

顯然,從配置文件的寫法上彷佛是沒法避免攔截器被切入的(雖然org.springframework.aop.framework.* 並非切面),那隻能在切點裏想辦法迴避了 博客

Java代碼   收藏代碼
  1. public void before(JoinPoint jp) throws Throwable {  
  2.     Object target = jp.getTarget();  
  3.     if(AopUtils.isAopProxy(target)){  
  4.         return;  
  5.     }  
  6.     ...  
  7. }  



至此,可讓多參數方法被切入的before切點完成了。

(after-returnning沒有遇到什麼障礙,多參數也很簡單,甚至不用在pointcut配置中聲明,還可取到返回值) string

Xml代碼   收藏代碼
  1. <aop:after-returning method="afterReturning" arg-names="retVal" returning="retVal" pointcut="execution(* cn.xxxx..*.*(..))"/>  



切點,能夠同時獲得JoinPoint和返回值

Java代碼   收藏代碼
  1. public void afterReturning(JoinPoint jp, Object retVal) throws Throwable {    
  2.     Object target = jp.getTarget();  
  3.     if(AopUtils.isAopProxy(target)){  
  4.         return;  
  5.     }  
  6.     ...  
  7. }  

最後,不知道哪位曉得,若是經過AOP而不是中間段落中提到的【攔截器】,來實現現有攔截器的功能,即取得入口參數,並能夠控制繼續執行被切的業務ServiceBean,仍是阻止它執行。

相關文章
相關標籤/搜索