最近在开发一个基础应用服务系统,利用加密的token标识来校验访问者的身份。几乎每个接口都须要校验token。故打算采用aop面向切面编程,一次性对全部接口进行身份认证;java
切面配置没有问题的状况下,junit单元测试调用controller里面的方法,能够触发切点,实现切面编程。可是web部署到tomcat后,直接url访问触发切点失败!web
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出现异常了......"); } }
/** * * @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
<!-- 自动扫描项目下面的包 ,将带有注解的类 归入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 部分代码片断express
<!-- 默认的注解映射的支持 --> <mvc:annotation-driven /> <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 --> <context:component-scan base-package="com.test.controller" />
@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里面的方法!!!
切点没有触发!!spring-mvc
通过查找资料及本身验证得出:
1.是父子容器的问题
2.个人切面代码和链接点,通知都没有问题,问题出在了配置信息上面。tomcat
<!-- 配置使Spring采用CGLIB代理 --> <aop:aspectj-autoproxy proxy-target-class="true" />
(部分借鉴)CGLIB代理配置在了applicationContext.xml 核心配置文件中,该配置文件会被ContextLoaderListenerclass加载,spring会建立一个WebApplicationContext上下文,称为父上下文(父容器) ,保存在ServletContext中,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。mvc
而spring-mvc.xml是DispatcherServlet,能够同时配置多个,每一个 DispatcherServlet有一个本身的上下文对象(WebApplicationContext),称为子上下文(子容器),子上下文能够访问父上下文中的内容,但父上下文不能访问子上下文中的内容。 它也保存在 ServletContext中,key是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名称app
当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扫描的)也不多。