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

posted @ 2020-05-15 09:35  幽暗森林之猪大屁  阅读(2230)  评论(0)    收藏  举报