@RequestMapping MethodInterceptor方面忽略@Validated注释

时间:2014-09-16 08:16:20

标签: java spring validation interceptor aspect

我对MethodInterceptor方面和@Validated注释有一个奇怪的问题。当我添加方法拦截器来拦截用@RequestMapping注释的所有方法时,@Validated注释不起作用。当我删除它,它再次正常工作。我想,我的拦截器会覆盖@Validated。有没有办法做到这一点,因为我不想在我的拦截器中执行验证,因为它是用于审计的东西。

这是验证器:

@Component
public class UserValidator implements Validator {

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

控制器的代码:

@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) {
    }
}

拦截器代码:

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

我的环境是带有休息控制器(Jackson Mapper)的Spring MVC(3.2)。

2 个答案:

答案 0 :(得分:1)

问题在于使用基于代理的AOP,在这种情况下,由于没有接口存在,它是基于类的代理。由于@RequestBody@Validated方法未被继承,因此它们不存在于覆盖该方法的子类中。因此不再有@Validated

要解决这个问题,有(对于这种情况)有两种解决方案。

  1. a HandlerInterceptor而不是AOP
  2. 监听ApplicationListener s
  3. ServletRequestHandledEvent
  4. 为所有包含注释的控制器添加接口
  5. 使用加载时间或编译时间而不是基于代理的AOP。
  6. 由于这只是性能测量,因此选项1和2非常容易。选项3可能非常具有侵入性,选项4取决于所使用的servlet容器。

    使用HandlerInterceptor

    然而,因为这只是一个性能测量并且适用于处理程序而不是AOP,我建议使用HandlerInterceptor,它将在preHandle中开始计时并以afterCompletion方法结束。

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

    使用ApplicationListener<ServletRequestHandlerEvent>

    由于DispatcherServlet已经监视处理请求所花费的时间,并且在处理请求之后会触发ServletRequestHandledEvent,其中包含处理所需的时间。您可以创建一个ApplicationListener来监听事件并将一行写入日志文件(或控制台)。

    @Component
    public class PerformanceListener implements ApplicationListener<ServletRequestHandledEvent> {
    
        public void onApplicationEvent(ServletRequestHandledEvent event) {
            System.out.println("Processing: " + event.getRequestUrl() + ", took: " + event.getProcessingTimeMillis + "ms. ");
        }
    }
    

    使用加载时编织

    使用加载时编织时,只要加载类就修改了类的字节码,因此无需为类创建代理。这允许@Validated仍然继续工作。

    根据所使用的servlet容器,可能就像用<aop:aspectj-autoproxy />@EnableAspectJAutoProxy替换<context:load-time-weaving />@EnableLoadTimeWeaving一样简单。对于其他容器,它需要设置javaagent以允许加载时织入。有关详细信息,请查看reference guide

    使用编译时编织

    使用编译时编织时,在项目的编译阶段修改类的字节码。这需要修改您的编译和ajc(AspectJ编译器)的使用。这可能很棘手,您可能需要查看Compile time weaving for DI in non-spring managed classes以获取更多相关信息。

    注意

    你的看点很奇怪,因为它混合了两种不同风格的AOP。首先它使用AspectJ,然后它尝试成为aopalliance MethodInterceptor。尽量不要混合方法,因为这可能会导致奇怪的问题。

    我建议坚持使用AspectJ,所以尝试修改你的Aspect也可能想要将stopStopWatch方法添加到finally,而不是仅添加到catch块。

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

    您可能想要查看弹簧已经提供的弹簧性能拦截器,而不是自己滚动。您可能希望使用AbstractMonitoringInterceptor的子类之一而不是您自己的子类。 (虽然这不能帮助您解决代理问题,但它可以帮助您维护自己的性能拦截器。)

答案 1 :(得分:0)

您应该检查Spring上下文和POM以获取以下元素:

  • Spring doc说您应该配置MethodValidationPostProcessor
  • MethodValidationPostProcessor delegates validation to a JSR-303 implementation,就像Hibernate一样。所以应该配置依赖项。这应该有效:

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