diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java index 697944e69a..bbcf92ea9e 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,7 +81,7 @@ public class MethodValidationInterceptor implements MethodInterceptor { } - private final Validator validator; + private volatile Validator validator; /** @@ -116,7 +116,18 @@ public class MethodValidationInterceptor implements MethodInterceptor { if (forExecutablesMethod != null) { // Standard Bean Validation 1.1 API - Object execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator); + Object execVal; + try { + execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator); + } + catch (AbstractMethodError err) { + // Probably an adapter (maybe a lazy-init proxy) without BV 1.1 support + Validator nativeValidator = this.validator.unwrap(Validator.class); + execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, nativeValidator); + // If successful, store native Validator for further use + this.validator = nativeValidator; + } + Method methodToValidate = invocation.getMethod(); Set> result; @@ -137,13 +148,11 @@ public class MethodValidationInterceptor implements MethodInterceptor { } Object returnValue = invocation.proceed(); - result = (Set>) ReflectionUtils.invokeMethod(validateReturnValueMethod, execVal, invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); } - return returnValue; } diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java index 812b1d9ae1..a6af80e722 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.validation.beanvalidation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import javax.validation.Validator; import javax.validation.constraints.Max; import javax.validation.constraints.NotNull; import javax.validation.groups.Default; @@ -26,6 +27,10 @@ import org.junit.Test; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.MutablePropertyValues; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.support.StaticApplicationContext; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor; @@ -64,7 +69,6 @@ public class MethodValidationTests { ac.close(); } - private void doTestProxyValidation(MyValidInterface proxy) { assertNotNull(proxy.myValidMethod("value", 5)); try { @@ -115,6 +119,13 @@ public class MethodValidationTests { } } + @Test + public void testLazyValidatorForMethodValidation() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + LazyMethodValidationConfig.class, CustomValidatorBean.class, MyValidBean.class); + ctx.getBean(MyValidInterface.class).myValidMethod("value", 5); + } + @MyStereotype public static class MyValidBean implements MyValidInterface { @@ -165,4 +176,16 @@ public class MethodValidationTests { public @interface MyValid { } + + @Configuration + public static class LazyMethodValidationConfig { + + @Bean + public static MethodValidationPostProcessor methodValidationPostProcessor(@Lazy Validator validator) { + MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor(); + postProcessor.setValidator(validator); + return postProcessor; + } + } + } diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java b/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java index af94ccb0af..14135a03c7 100644 --- a/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java +++ b/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.validation.hibernatevalidator5; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import javax.validation.Validator; import javax.validation.constraints.Max; import javax.validation.constraints.NotNull; import javax.validation.groups.Default; @@ -26,11 +27,16 @@ import org.junit.Test; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.MutablePropertyValues; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.support.StaticApplicationContext; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor; import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor; import org.springframework.validation.annotation.Validated; +import org.springframework.validation.beanvalidation.CustomValidatorBean; import org.springframework.validation.beanvalidation.MethodValidationInterceptor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; @@ -68,7 +74,6 @@ public class MethodValidationTests { ac.close(); } - @SuppressWarnings("unchecked") private void doTestProxyValidation(MyValidInterface proxy) { assertNotNull(proxy.myValidMethod("value", 5)); @@ -120,6 +125,13 @@ public class MethodValidationTests { } } + @Test + public void testLazyValidatorForMethodValidation() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + LazyMethodValidationConfig.class, CustomValidatorBean.class, MyValidBean.class); + ctx.getBean(MyValidInterface.class).myValidMethod("value", 5); + } + @MyStereotype public static class MyValidBean implements MyValidInterface { @@ -170,4 +182,16 @@ public class MethodValidationTests { public @interface MyValid { } + + @Configuration + public static class LazyMethodValidationConfig { + + @Bean + public static MethodValidationPostProcessor methodValidationPostProcessor(@Lazy Validator validator) { + MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor(); + postProcessor.setValidator(validator); + return postProcessor; + } + } + }