How to get method execution flow in spring mvc interceptor

两盒软妹~` 提交于 2019-12-11 05:13:28

问题


I want to get the complete execution flow along with their execution time in spring mvc project.

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);
}

}

I am getting controller classes and method with @Metrics annotation to log metrics of few methods. What i want is to get entire method execution flow(Controller-->Service-->DAO) along time spent in each method. Is there anyway to get that information in postHandle() or afterCompletion(). Please suggest.


回答1:


Using Spring AOP Around advice this can be achieved.

Write a pointcut which will intercept

  • All execution of public method(s) in package & its sub-packages
  • And belong to packages & its sub-packages for
    • Controller layer
    • Service layer (optionally restrict to a specific service only)
    • DAO layer

Then write the Around Advice as below

@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;
    }
}

After necessary configurations the sample output should look like below

Method com.test.dao.TestDAO.getTest took 350
Method com.test.service.TestService.getTest took 1954
Method com.test.controller.TestController.getTest took 3751

Alternatively three pointcuts and respective Around advice can be written each intercepting specific package so as to do away with the if-else part which checks the controller

I have tested the setup with

  • Spring 4.3.2 Release
  • AspectJ 1.7.4
  • JDK 1.8
  • Tomcat 8.0

Let know in comments if there are any issues.



来源:https://stackoverflow.com/questions/40153833/how-to-get-method-execution-flow-in-spring-mvc-interceptor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!