0

I have a strange problem with MethodInterceptor aspect and @Validated annotation. When I add the method interceptor to intercept all the methods annotated with @RequestMapping, the @Validated annotation does not work. When I remove it, it works fine again. I think, my interceptor overrides the @Validated. Is there are any way to do this as I don't want to perform validation in my interceptor, because it is meant for auditing stuff.

Here is the validator:

@Component
public class UserValidator implements Validator {

@Override
public void validate(Object target, Errors errors) {
          //validation stuff
      }
}

The code of the controller:

@Controller
@RequestMapping("/profile")
public class UserProfileController {

    @Autowired
    private UserValidator userValidator;

    @InitBinder("userValidator")
    private void userValidatorInitBinder(WebDataBinder binder) {
        binder.setValidator(userValidator);
    }

    @RequestMapping(value = "/reg.do", method = RequestMethod.POST)
    public @ResponseBody RegisterResponse reg(HttpServletRequest httpServletRequest, @RequestBody @Validated CreateUserRequest createUserRequest) {
    }
}

The Interceptor code:

@Aspect
@Component
public class MyInterceptor implements MethodInterceptor, Serializable {

    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Object returnValue = null;
        final StopWatch stopWatch = new StopWatch();
        try {
           stopWatch.start();
           returnValue = invocation.proceed();
           return returnValue;
       } catch (Throwable ex) {
           stopStopwatch(stopWatch);
           throw ex;
       } finally {
           System.out.println(stopwatch);
}
    }
}

My environment is Spring MVC (3.2) with rest controllers (Jackson Mapper).

  • Your aspect leads to a proxy of the actual controller. This proxy might be problematic in detecting the actual annotations. Also why is the `UserValidator` also the object used in the binding!!! Also post the code of your interceptor. – M. Deinum Sep 16 '14 at 08:36
  • @M.Deinum the 'UserValidator' is a singleton instance and I register it in the binder because I have multiaction controller so I have multiple validators, also the code inside the interceptor just calculates the time of the request and 'return invocation.proceed();' – Mohamed Atiea Sep 16 '14 at 09:45
  • Please read my comment... You are using your validator as a model object... This `@RequestBody @Validated UserValidator userValidator` is part of your method signature... I highly doubt that the incoming request will turn into a `UserValidator` it wold probably be more a `User`... And as stated, add the imlpementation of the aspect... – M. Deinum Sep 16 '14 at 09:47
  • @M.Deinum yeah I've noticed and updated the thread but this is not the issue the actual code uses CreateUserRequest (updated above) without any annotations. – Mohamed Atiea Sep 16 '14 at 09:51
  • And requesting again please add the code for your aspect... Which actually looks weird as you are mixing both AspectJ style and aopalliance style. Select one don't mix. – M. Deinum Sep 16 '14 at 09:53
  • @M.Deinum I've added the code in the aspect. and about the mix, I've tried aopalliance and AspectJ both as stand alone and and as mix and it dose not work too. – Mohamed Atiea Sep 16 '14 at 09:54
  • Could you add which Spring version you are using? As there have been numerous fixes in this area (proxying `@RequestMapping` methods`) done in the Spring 3.1 series. So I would at least try one of the newest Spring versions, maybe that would help/improve. – M. Deinum Sep 16 '14 at 10:57

2 Answers2

1

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.

  1. a HandlerInterceptor instead of a AOP
  2. an ApplicationListener which listens to ServletRequestHandledEvents
  3. add an interface to all your controllers which also contain the annotations
  4. 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).

Community
  • 1
  • 1
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • I've already done this yesterday but still not working, and about the Handler interceptor I know about it but the scope of the interceptor is beyond time tracking, it is auditing for every request and response (JSON) so method arguments was already binded I know that HandelerInterceptor can do so but with extra code. I'll go for normal filter, thanks anyway :) – Mohamed Atiea Sep 16 '14 at 11:03
  • Depending on what you need (if it is only logging) Spring has the `AbstractRequestLoggingFilter` which logs the request including the payload etc. if configured correctly. As I stated you will run into problems with the proxy approach due to subclassing. – M. Deinum Sep 16 '14 at 12:34
0

You should check your Spring context and POM for the following elements:

  • Spring doc says you should configure a MethodValidationPostProcessor
  • The MethodValidationPostProcessor delegates validation to a JSR-303 implementation, like that of Hibernate. So dependencies should be configured. This should work:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.0.1.Final</version>
    </dependency>
    
Daniel Cerecedo
  • 6,071
  • 4
  • 38
  • 51
  • It was working fine without any extra configurations and whenever I remove the interceptor it works fine again, also I am not using hibernate validation annotation I am using spring validators which dose not need the above 2 jars. – Mohamed Atiea Sep 16 '14 at 09:47