The problem is the fact that proxy based AOP is used and in this case it is a classbased proxy due to no interface being present. As the @RequestBody
and @Validated
method aren't inherited they aren't present on the subclass that overrides the method. Hence no more @Validated
.
To fix this there are (for this case) a couple of solutions.
- a
HandlerInterceptor
instead of a AOP
- an
ApplicationListener
which listens to ServletRequestHandledEvent
s
- add an interface to all your controllers which also contain the annotations
- Use loadtime or compile timeweaving instead of proxy based AOP.
As this is only a performance measurment it is quite easy to do with option 1 and 2. Option 3 is probably quite invasive and option 4 depends on the servlet container being used.
Using a HandlerInterceptor
However as this is only a performance measurement and applies to handlers instead of AOP I suggest using a HandlerInterceptor
which would start timing in the preHandle
and end in the afterCompletion
method.
public class PerformanceMeasureInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
StopWatch sw = new StopWatch();
sw.start();
request.setAttribute("performance-measure-sw", sw);
return true;
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
StopWatch sw = (StopWatch) request.getAttribute("performance-measure-sw");
if (sw != null) {
sw.stop();
System.out.println(sw);
}
}
}
Using an ApplicationListener<ServletRequestHandlerEvent>
As the DispatcherServlet
already monitors the time it takes to proces a request and after handling a request fires a ServletRequestHandledEvent
which contains the time it took to process. You can create an ApplicationListener
which listens to the events and writes a line to a log file (or console).
@Component
public class PerformanceListener implements ApplicationListener<ServletRequestHandledEvent> {
public void onApplicationEvent(ServletRequestHandledEvent event) {
System.out.println("Processing: " + event.getRequestUrl() + ", took: " + event.getProcessingTimeMillis + "ms. ");
}
}
Using load-time weaving
When using load-time weaving the bytecode of a class is modified as soon as a class is loaded, hence no need to create a proxy for the class. This allows for the @Validated
to still keep working.
Depending on the servlet container being used it might be as easy as replacing a <aop:aspectj-autoproxy />
or @EnableAspectJAutoProxy
with <context:load-time-weaving />
or @EnableLoadTimeWeaving
. For other containers it requires setting up a javaagent to allow load-time weaving to happen. For more information you want to check the reference guide
Using compile-time weaving
When using compile-time weaving the bytecode of a class is modified in the compilation phase of your project. This requires modifications to your compilation and the use of ajc
(AspectJ compiler). This can be tricky to get working, you might want to check Compile time weaving for DI in non-spring managed classes for more info on this.
Note
Your Aspect is weird as it is mixing 2 different styles of AOP. First it uses AspectJ and second it tries to be a aopalliance MethodInterceptor
. Try not to mix approaches as that might result in weird issues.
I suggest sticking with AspectJ so try to modify your Aspect also you probably want to add the stopStopWatch
method to the finally instead of only to the catch
block.
@Aspect
@Component
public class MyInterceptor {
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
Object returnValue = null;
final StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
returnValue = pjp.proceed();
return returnValue;
} catch (Throwable ex) {
throw ex;
} finally {
stopStopwatch(stopWatch);
System.out.println(stopwatch);
}
}
}
Instead of rolling your own you might want to checkout the spring performance interceptors spring already provides. You might want to use one of the subclasses of AbstractMonitoringInterceptor
instead of your own. (Although that doesn't help you solve the proxying problem it helps you maintaining your own performance interceptor).