业务场景:
目前在开发一个web后端的程序,提供基于spring mvc的restful的接口给手机端调用。由于接口对性能和效率的要求比较高,为了更好的分析问题,这边决定输出日志对接口调用情况,以及频率,做记录。
这边考虑基于spring的aop来实现日志切面编程,下面看代码:
切面类
@Aspect
public class CxxxAspect {
/**
* 定义一个切入点
*/
@Pointcut("execution(* com..controller.*Controller.*Auth(..))")
private void pointCutMethod() {
}
/**
* 声明前置通知
*/
@Before("pointCutMethod()")
public void doBefore() {
System.out.println("前置通知");
}
/**
* 声明后置通知
* @param result
*/
@AfterReturning(pointcut = "pointCutMethod()", returning = "result")
public void doAfterReturning(String result) {
System.out.println("后置通知");
System.out.println("---" + result + "---");
}
/**
* 声明例外通知
*/
@AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
public void doAfterThrowing(Exception e) {
System.out.println("例外通知");
System.out.println(e.getMessage());
}
/**
* 声明最终通知
*/
@After("pointCutMethod()")
public void doAfter() {
System.out.println("最终通知");
}
/**
* 声明环绕通知
* @param pjp
* @return
* @throws Throwable
*/
@Around("pointCutMethod()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入方法---环绕通知");
Object o = pjp.proceed();
System.out.println("退出方法---环绕通知");
return o;
}
}
目标类
@Controller
@RequestMapping(value="xxxx")
@SuppressWarnings({ "rawtypes", "unchecked" })
public class CXXXController{
@ResponseBody
@RequestMapping(value="/xxxx",method=RequestMethod.POST)
public void xxxAuth(HttpServletRequest request){
//省略实际业务代码
return result;
}
}
配置文件信息(保护个人隐私类名全路径以xxxx代替)
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="caFaceAuthAspect" class="com.xxxxx.aspect.CxxxAspect"/>
通过main方法测试代码:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
context.start();
CAAuthController con=(CXXXController)context.getBean(CXXXController.class);
con.xxxAuth(null, null);
日志输出:
进入方法---环绕通知
前置通知
最终通知
例外通知
null
证明当前配置已经ok,测试效果达到预期。但是用tomcat启动时,通过http访问到controll时,并没有进入切面类,更别说通知方法了,请问这是什么情况。
我有一个模糊的假设就是,我在main方法里面是手动实实在在的用controll类去调用了方法,所以触发了通知的连接点条件,但是http访问时,通过spring的mvc分发,反射调用所以并没有触发连接点的条件。
不知道我的假设是否正确,或者有相关的大神还请不吝赐教。
好了,經過各大論壇發帖,各大技術群求大神,終於找到了問題的原因:
1.是父子容器的問題
2.我的切面代碼和連接點,通知都沒有問題,問題出在了我的配置資訊上面。
3.我將設定資訊如下:
將其設定在了spring-context.xml 核心設定檔中,該設定檔會被ContextLoaderListenerclass加在,Spring會建立一個WebApplicationContext上下文,稱為父上下文(父容器) ,儲存在ServletContext中,keyWebApplicationContext. 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中,那麼問題就解決了。我已經測試通過啦。