springmvc中aop對controller切面編程


概述:
最近在開發一個基礎應用服務系統,利用加密的token標識來校驗訪問者的身份。幾乎每一個接口都需要校驗token。故打算采用aop面向切面編程,一次性對所有接口進行身份認證;
遇見的問題:
切面配置沒有問題的情況下,junit單元測試調用controller里面的方法,可以觸發切點,實現切面編程。但是web部署到tomcat后,直接url訪問觸發切點失敗!

[Java]  純文本查看 復制代碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
/**
  *
  * @Description: TODO(  token的校驗    )
  * @author: mayao
  * @date 2016年10月20日 下午5:47:37
  */
@Aspect
@Component
public class UserTokenInterceptor {
 
     //controller包的子包里面任何方法
     @Pointcut ( "execution(public * com.test.controller.*.*(..))" )
     public void checkToken(){
     }
 
     @Before ( "checkToken()" )
     public void beforeCheckToken(){
         System.out.println( "調用方法之前。。。。" );
     }
 
     @AfterReturning ( "checkToken()" )
     public void afterCheckToken(){
         System.out.println( "調用方法結束之后。。。。" );
     }
 
     //拋出異常時才調用 
     @AfterThrowing ( "checkToken()"
     public void afterThrowing() 
    
         System.out.println( "校驗token出現異常了......" ); 
     }
}


控制器:

[Java]  純文本查看 復制代碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
/**
  *
  * @Description: TODO( 請求token )
  * @author: mayao
  * @date 2016年10月19日 下午5:11:25
  */
 
@Controller
@RequestMapping ( "/mayao" )
public class TokenController {
 
     @RequestMapping (value= "/test" ,method=RequestMethod.GET)
     public void test(){
         System.out.println( "調用controller里面的方法!!!" );
     }
 
}


配置文件:
applicationContext.xml 部分代碼片段
    <!-- 自動掃描項目下面的包 ,將帶有注解的類 納入spring容器管理   掃描service、dao -->
    <context:component-scan base-package="com.test"></context:component-scan>
    <!-- 配置使Spring采用CGLIB代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
spring-mvc.xml 部分代碼片段
    <!-- 默認的注解映射的支持 -->
    <mvc:annotation-driven />
    <!-- 自動掃描該包,使SpringMVC認為包下用了@controller注解的類是控制器 -->
    <context:component-scan base-package="com.test.controller" />
junit測試代碼:

[Java]  純文本查看 復制代碼
?
1
2
3
4
5
6
7
@Test
  public void controllerAOPTest(){
      ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath*:applicationContext.xml" );
      ctx.start();
      TokenController token = (TokenController)ctx.getBean(TokenController. class );
      token.test();
  }


測試結果:
調用方法之前。。。。
調用controller里面的方法!!!
調用方法結束之后。。。。
測試時成功的!!
然后啟動tomcat,訪問鏈接 http://localhost/項目名/mayao/test,結果是:調用controller里面的方法!!! 
切點沒有觸發!!
原因:
經過查找資料及自己驗證得出: 
1.是父子容器的問題 
2.我的切面代碼和連接點,通知都沒有問題,問題出在了配置信息上面。
    <!-- 配置使Spring采用CGLIB代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" /> 
(部分借鑒)CGLIB代理配置在了applicationContext.xml 核心配置文件中,該配置文件會被ContextLoaderListenerclass加載,Spring會創建一個WebApplicationContext上下文,稱為父上下文(父容器) ,保存在ServletContext中,key為WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。
而spring-mvc.xml是DispatcherServlet,可以同時配置多個,每個 DispatcherServlet有一個自己的上下文對象(WebApplicationContext),稱為子上下文(子容器),子上下文可以訪問父上下文中的內容,但父上下文不能訪問子上下文中的內容。 它也保存在 ServletContext中,key是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名稱
當spring加在父容器的時候就會去找切入點,但是這個時候切入的controller是在子容器中的,父容器是無法訪問子容器,所以就攔截不到。如果將上述的配置文件放到spring-mvc.xml中,那么問題就解決了。我已經測試可以通過URL訪問觸發切點了。
解決方法:
將配置信息:
    <!-- 配置使Spring采用CGLIB代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" /> 
從applicationContext.xml移到spring-mvc.xml中就可以了
總結:
歸根結底來說,其實是bean的代理問題,涉及普通的bean,service,controller。下面的要注意。
Spring MVC 和 Spring 整合的時候,SpringMVC的spring-mvc.xml文件中配置掃描包,不要包含 service的注解,Spring的applicationContext.xml文件中配置掃描包時,不要包含controller的注解。
錯誤如下:
<!-- spring-mvc.xml  -->
<context:component-scan base-package="com.test">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
<!-- applicationContext.xml  -->
<context:component-scan base-package="com.test">           
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
Spring MVC啟動時的配置文件,包含組件掃描、url映射以及設置freemarker參數,讓spring不掃描帶有@Service注解的類。為什么要這樣設置?
因為spring-mvc.xml與applicationContext.xml不是同時加載,如果不進行這樣的設置,那么,spring就會將所有帶@Service注解的類都掃描到容器中,等到加載applicationContext.xml的時候,會因為容器已經存在Service類,使得cglib將不對Service進行代理,直接導致的結果就是在applicationContext 中的事務配置不起作用,發生異常時,無法對數據進行回滾。以上就是原因所在。
不過不必太當心這個,實際應用中這樣的配置(指定service,controller掃描的)也很少。

更多免費技術資料可關注:annalin1203


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM