1. 程式人生 > >MethodInterceptor攔截器 加註解精準攔截method

MethodInterceptor攔截器 加註解精準攔截method

ps :這種方式 攔截的力度比較細 ,並且是可以自定義哪些具體方法被攔截

1.自定義一個annotation

[java] view plain copy  print?

  1. package com.websystem.util;  
  2. import java.lang.annotation.Documented;  
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7. /** 
  8.  * des:自定義使方法跳過攔截的註解 
  9.  * author: zbl 
  10.  * date: 2014年9月3日 
  11.  **/  
  12. @Target(ElementType.METHOD)  
  13. @Retention(RetentionPolicy.RUNTIME)  
  14. @Documented  
  15. public abstract @interface RequiredInterceptor{  
  16.     boolean required() default true;  
  17. }  


2.在Controller裡面的方法使用自定義的annotation,下面是一個登進登出的例子。

[java] view plain copy  print?

  1. package com.websystem.controller;  
  2. import java.util.HashMap;  
  3. import java.util.Map;  
  4. import javax.annotation.Resource;  
  5. import org.springframework.beans.factory.annotation.Value;  
  6. import org.springframework.stereotype.Controller;  
  7. import org.springframework.ui.ModelMap;  
  8. import org.springframework.web.bind.annotation.ModelAttribute;  
  9. import org.springframework.web.bind.annotation.RequestMapping;  
  10. import org.springframework.web.bind.annotation.RequestMethod;  
  11. import org.springframework.web.bind.annotation.ResponseBody;  
  12. import org.springframework.web.bind.annotation.SessionAttributes;  
  13. import org.springframework.web.bind.support.SessionStatus;  
  14. import org.springframework.web.servlet.ModelAndView;  
  15. import org.springframework.web.servlet.view.RedirectView;  
  16. import com.websystem.model.ManagerModel;  
  17. import com.websystem.service.ManagerService;  
  18. import com.websystem.util.AESPlusHelper;  
  19. import com.websystem.util.Constant;  
  20. import com.websystem.util.RequiredInterceptor;  
  21. /** 
  22.  * des: 
  23.  * author: zbl 
  24.  * date: 2014年8月26日 
  25.  **/  
  26. @Controller  
  27. @SessionAttributes(Constant.Manager)  
  28. public class ManagerController {  
  29.     @Resource  
  30.     private ManagerService managerServiceImpl;  
  31.     @RequiredInterceptor(required = false)  
  32.     @RequestMapping(value = "manager/login.do",method = RequestMethod.GET)    
  33.     public ModelAndView login(ManagerModel managerModel,ModelMap model){  
  34.         ManagerModel manager = managerServiceImpl.getManager(managerModel);  
  35.         if(manager!=null){  
  36.             manager.setPassword("");  
  37.             model.addAttribute(Constant.Manager, manager);  
  38.             return new ModelAndView(new RedirectView(Constant.MainURL));  
  39.         }else{  
  40.             return new ModelAndView(new RedirectView(Constant.LoginURL));  
  41.         }  
  42.     }  
  43.     @RequiredInterceptor(required = false)  
  44.     @RequestMapping(value = "manager/logout.do",method = RequestMethod.GET)  
  45.     @ResponseBody  
  46.     public Object logout(SessionStatus status){  
  47.         status.setComplete();  
  48.         Map<String,Object> map = new HashMap<String,Object>();  
  49.         map.put(Constant.Success, true);  
  50.         return map;  
  51.     }  
  52. }  


3.定義MethodInterceptor,裡面可以處理AOP邏輯程式碼。

[java] view plain copy  print?

  1. package com.websystem.util;  
  2. import org.aopalliance.intercept.MethodInterceptor;  
  3. import org.aopalliance.intercept.MethodInvocation;  
  4. import org.springframework.core.annotation.AnnotationUtils;  
  5. import org.springframework.stereotype.Component;  
  6. /** 
  7.  * des: 
  8.  * author: zbl 
  9.  * date: 2014年9月3日 
  10.  **/  
  11. @Component  
  12. public class SessionInterceptor implements MethodInterceptor {  
  13.     @Override  
  14.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  15.         // TODO Auto-generated method stub  
  16.         RequiredInterceptor requiredInterceptor = AnnotationUtils.findAnnotation(invocation.getMethod(), RequiredInterceptor.class);  
  17.         if(requiredInterceptor!=null){  
  18.             System.out.println(invocation.getMethod().getName());  
  19.             //你要做的邏輯程式碼  
  20.         }  
  21.         return invocation.proceed();  
  22.     }  
  23. }  

4.新增配置

[html] view plain copy  print?

  1. <context:component-scan base-package="com.websystem.controller,com.websystem.*.impl,com.websystem.util"/>  
  2.     <mvc:annotation-driven/>  
  3.     <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  4.         <property name="beanNames">  
  5.             <list><value>*Controller</value></list>  
  6.         </property>  
  7.         <property name="interceptorNames">  
  8.             <list><value>sessionInterceptor</value></list>  
  9.         </property>  
  10.     </bean>  

另外一篇

實現MethodInterceptor 介面,在呼叫目標物件的方法時,就可以實現在呼叫方法之前、呼叫方法過程中、呼叫方法之後對其進行控制。

MethodInterceptor 介面可以實現MethodBeforeAdvice介面、AfterReturningAdvice介面、ThrowsAdvice介面這三個介面能夠所能夠實現的功能,但是應該謹慎使用MethodInterceptor 介面,很可能因為一時的疏忽忘記最重要的MethodInvocation而造成對目標物件方法呼叫失效,或者不能達到預期的設想。

關於含有Advice的三種對目標物件的方法的增強,可以參考文章在Spring的IOC容器中裝配AOP代理 。

在Spring的IOC容器中裝配AOP代理 的基礎上,比較MethodInterceptor 介面的實現與上面提及到的三種介面實現對目標物件方法的增強的功能效果。

我們將從應用中分離出日誌切面,,將對日誌的操作整合到實現MethodInterceptor 介面的類SpringMethodInterceptor中,該實現類的程式碼如下所示:

package org.shirdrn.spring.aop;

import java.util.Date;

import org.aopalliance.intercept.MethodInterceptor ;
import org.aopalliance.intercept.MethodInvocation;

public class SpringMethodInterceptor implements MethodInterceptor {

public Object invoke(MethodInvocation invo) throws Throwable {
   Object[] object = invo.getArguments();
   try{
    String date1 = (new Date()).toLocaleString();
    System.out.println("資訊:[MethodInterceptor ]["+date1+"]使用者 "+object[0]+" 正在嘗試登入陸系統...");
   
    Object returnObject = invo.proceed();
   
    String date2 = (new Date()).toLocaleString();
    System.out.println("資訊:[MethodInterceptor ]["+date2+"]使用者 "+object[0]+" 成功登入系統.");
   
    return returnObject;
   }
   catch(Throwable throwable){
    if(object[0].equals("Jessery")){
     throw new Exception("資訊:[MethodInterceptor ]不允許黑名單中使用者 "+object[0]+" 登入系統");
    }
   }
   return object;
}

}

程式中,紅色標示的程式碼行Object returnObject = invo.proceed();很關鍵,只有通過它來對目標物件方法呼叫,返回一個Object物件。

呼叫目標物件方法之前,可以新增跟蹤日誌,對方法增強,相當於使用MethodBeforeAdvice介面對方法進行增強。

呼叫目標物件方法之後,也可以新增跟蹤日誌,對方法增強,相當於使用AfterReturningAdvice介面對方法進行增強。

在執行目標物件方法的過程中,如果發生異常,可以在catch中捕獲異常,相當於使用ThrowsAdvice介面對方法進行增強。

上面實現了AOP,同時要在XML中裝配,配置如下所示:

<bean id="springMethodInterceptor"
   class="org.shirdrn.spring.aop.SpringMethodInterceptor"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
</bean>
<bean id="accountService"
   class="org.springframework.aop.framework.ProxyFactoryBean"
   abstract="false" singleton="true" lazy-init="default"
   autowire="default" dependency-check="default">
   <property name="target">
    <ref bean="accountServiceImpl" />
   </property>
   <property name="interceptorNames">
    <list>
     <value>loginMethodBeforeAdvice</value>
     <value>loginAfterReturningAdvice</value>
     <value>loginThrowsAdvice</value>
     <value>springMethodInterceptor</value>
    </list>
   </property>
</bean>

紅色標示部分為對使用MethodInterceptor 對目標物件方法進行增強的配置。

package org.shirdrn.main;

import org.shirdrn.interf.AccountServiceInterf;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

public static void main(String[] args) {
   String name = "shirdrn";
   String pwd = "830119";
   ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
   AccountServiceInterf asi = (AccountServiceInterf)ctx.getBean("accountService");
   asi.login(name, pwd);
}
}

執行輸出結果如下所示:

資訊:[2008-3-23 17:39:38]使用者 shirdrn 正在嘗試登入陸系統...
資訊:[MethodInterceptor ][2008-3-23 17:39:39]使用者 shirdrn 正在嘗試登入陸系統...
資訊:[MethodInterceptor ][2008-3-23 17:39:42]使用者 shirdrn 成功登入系統.
資訊:[2008-3-23 17:39:42]使用者 shirdrn 成功登入系統.

可見,標示為[MethodInterceptor ]的輸出資訊,就是MethodInterceptor 對呼叫目標物件方法的增強的結果。

如果我們使用非法的使用者帳戶登入系統:

String name = "Jessery";
   String pwd = "jessery";

就會被MethodInterceptor 攔截器攔截,而且丟擲異常,如下所示:

資訊:[2008-3-23 17:52:18]使用者 Jessery 正在嘗試登入陸系統...
資訊:[MethodInterceptor ][2008-3-23 17:52:18]使用者 Jessery 正在嘗試登入陸系統...
log4j:WARN No appenders could be found for logger (org.springframework.core.CollectionFactory).
log4j:WARN Please initialize the log4j system properly.
資訊:[MethodInterceptor ][2008-3-23 17:52:24]使用者 Jessery 登入失敗.
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at org.shirdrn.impl.AccountServiceImpl

EnhancerByCGLIB

32dd7c51.login(<generated>)
at org.shirdrn.main.Main.main(Main.java:16)
使用者登入過程中發生異常: Exception
Caused by: java.lang.Exception: 資訊:[MethodInterceptor ]不允許黑名單中使用者 Jessery 登入系統.
at org.shirdrn.spring.aop.SpringMethodInterceptor.invoke(SpringMethodInterceptor.java:27)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:51)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:623)
... 2 more

我們可以看到,使用MethodInterceptor 攔截器打印出了三項相關資訊:

呼叫目標物件的方法之前,對其進行了增強:

資訊:[MethodInterceptor ][2008-3-23 17:52:18]使用者 Jessery 正在嘗試登入陸系統...

因為不允許非法使用者Jessery登入系統,即不允許Jessery呼叫login方法,故在呼叫login方法過程中丟擲了異常,並且進行了日誌跟蹤:

資訊:[MethodInterceptor ][2008-3-23 17:52:24]使用者 Jessery 登入失敗.
Caused by: java.lang.Exception: 資訊:[MethodInterceptor ]不允許黑名單中使用者 Jessery 登入系統.

總結:

使用Spring的Bean裝配AOP,對於MethodBeforeAdvice介面、AfterReturningAdvice介面、ThrowsAdvice介面這三個介面在XML配置檔案中配置的順序對呼叫目標物件的方法沒有關係。

但是如果在使用上述的基礎上又使用了MethodInterceptor ,如果MethodInterceptor 配置順序不同,就可能將對目標物件方法的呼叫進行攔截,使得我們預期設想的使用AfterReturningAdvice對方法呼叫之後增強失效。

因此,如果兩類Advice同時使用,在裝配的時候,在XML配置檔案中,將MethodInterceptor 的配置放在其他三種Advice的後面,使得前三種Advice先起作用,最後使用MethodInterceptor 進行攔截。

[html] view plain copy  print?

  1. <context:component-scan base-package="com.websystem.controller,com.websystem.*.impl,com.websystem.util"/>  
  2.     <mvc:annotation-driven/>  
  3.     <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  4.         <property name="beanNames">  
  5.             <list><value>*Controller</value></list>  
  6.         </property>  
  7.         <property name="interceptorNames">  
  8.             <list><value>sessionInterceptor</value></list>  
  9.         </property>  
  10.     </bean>  

另外一篇