问题
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