0

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

我的环境是带有休息控制器(杰克逊映射器)的 Spring MVC(3.2)。

4

2 回答 2

1

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

为了解决这个问题,(对于这种情况)有几个解决方案。

  1. aHandlerInterceptor而不是 AOP
  2. 一个ApplicationListenerServletRequestHandledEvents 的
  3. 向所有包含注释的控制器添加一个接口
  4. 使用加载时间或编译时间编织而不是基于代理的 AOP。

由于这只是一个性能测量,因此使用选项 1 和 2 很容易。选项 3 可能非常具有侵入性,选项 4 取决于所使用的 servlet 容器。

用一个HandlerInterceptor

然而,由于这只是一个性能测量,并且适用于处理程序而不是 AOP,我建议使用HandlerInterceptor将在方法中开始计时preHandle并在方法中结束的a 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包含处理所需时间的 a。您可以创建一个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 />or@EnableAspectJAutoProxy替换<context:load-time-weaving />or一样简单@EnableLoadTimeWeaving。对于其他容器,它需要设置一个 javaagent 以允许发生加载时编织。有关更多信息,您想查看参考指南

使用编译时编织

当使用编译时编织时,类的字节码在项目的编译阶段被修改。这需要修改你的编译和使用ajc(AspectJ 编译器)。这可能很难开始工作,您可能需要检查Compile time weaving for DI in non-spring managed classes以获取更多信息。

笔记

您的 Aspect 很奇怪,因为它混合了 2 种不同风格的 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);
        }
    }
}

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

于 2014-09-16T10:08:12.613 回答
0

您应该检查 Spring 上下文和 POM 中的以下元素:

  • Spring doc说你应该配置一个MethodValidationPostProcessor
  • MethodValidationPostProcessor 验证委托给 JSR-303 实现,例如 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>
    
于 2014-09-16T08:38:10.837 回答