如何在spring mvc拦截器中获取方法执行流程

时间:2016-10-20 11:59:53

标签: java spring spring-mvc spring-aop

我想在spring mvc项目中获得完整的执行流程及其执行时间。

public class MetricsInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (handler instanceof HandlerMethod) {
        HandlerMethod hm = (HandlerMethod) handler;
        Method method = hm.getMethod();
        if (method.getDeclaringClass().isAnnotationPresent(Controller.class)) {
            if (method.isAnnotationPresent(Metrics.class)) {
               // System.out.println(method.getAnnotation(Metrics.class).value());
                System.out.println(method.getName());
            }
        }
    }
    return super.preHandle(request, response, handler);
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    super.postHandle(request, response, handler, modelAndView);
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    super.afterCompletion(request, response, handler, ex);
}

}

我正在使用@Metrics注释获取控制器类和方法来记录几种方法的指标。我想要的是在每个方法花费的时间内获得整个方法执行流程(Controller - > Service - > DAO)。无论如何在postHandle()或afterCompletion()中获取该信息。请建议。

1 个答案:

答案 0 :(得分:0)

使用Spring AOP Around建议可以实现。

写一个pointcut来拦截

      
  • 包裹中的所有公共方法的执行&其子包
  •   
  • 属于包装&
  • 的子包       
    • 控制器层
    • 服务层(可选择仅限于特定服务)
    • DAO层

然后编写 Around Advice ,如下所示

@Component
@Aspect
public class TraceAdvice {

    @Around("execution(* com.test..*(..))  &&" + " (within(com.test.controller..*) || "
        + "(within(com.test.service..*) && this(com.test.service.TestService)) || " + "within(com.test.dao..*))")
    public Object traceCall(ProceedingJoinPoint pjp) throws Throwable {
        /* This will hold our execution details in reverse order 
         * i.e. last method invoked would appear first.
         * Key = Full qualified name of method
         * Value = Execution time in ms
         */
        Map<String, Long> trace = null;
        Signature sig = pjp.getSignature();
        // Get hold of current request
        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // check if we are in controller (I used RestController modify it if Controller is required instead) 
        boolean isController = sig.getDeclaringType().isAnnotationPresent(RestController.class);
        if (isController) {
            // set the request attributte if we are in controller
            trace = new LinkedHashMap<>();
            req.setAttribute("trace", trace);
        } else {
            // if its not controller then read from request atributte
            trace = (Map<String, Long>) req.getAttribute("trace");
        }
        Object result = null;
        StopWatch watch = new StopWatch();
        try {
            // start the timer and invoke the advised method
            watch.start();
            result = pjp.proceed();
        } finally {
            // stop the timer 
            watch.stop();
            String methodName = sig.getDeclaringTypeName() + "." + sig.getName();
            // make entry for the method name with time taken
            trace.put(methodName, watch.getTotalTimeMillis());
            if (isController) {
                // since controller method is exit point print the execution trace
                trace.entrySet().forEach(entry -> {
                    System.out.println("Method " + entry.getKey() + " took " + String.valueOf(entry.getValue()));
                });
            }
        }
        return result;
    }
}

在必要的配置之后,示例输出应如下所示

  

方法com.test.dao.TestDAO.getTest取350   
方法com.test.service.TestService.getTest历时1954年   
方法com.test.controller.TestController.getTest取3751

或者,可以为每个拦截特定包编写三个切入点和相应的Around建议,以便取消检查控制器的if-else部分

我已经用

测试了设置
  • Spring 4.3.2 Release
  • AspectJ 1.7.4
  • JDK 1.8
  • Tomcat 8.0

如果有任何问题,请在评论中告知。