我对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)。
答案 0 :(得分:1)
问题在于使用基于代理的AOP,在这种情况下,由于没有接口存在,它是基于类的代理。由于@RequestBody
和@Validated
方法未被继承,因此它们不存在于覆盖该方法的子类中。因此不再有@Validated
。
要解决这个问题,有(对于这种情况)有两种解决方案。
HandlerInterceptor
而不是AOP ApplicationListener
s ServletRequestHandledEvent
由于这只是性能测量,因此选项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以获取以下元素:
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>