Skip to content

Commit 7118fcf

Browse files
committed
MethodValidationInterceptor falls back to invocation attempt with resolved bridge method (for Hibernate Validator 5.2 compatibility)
Issue: SPR-12237
1 parent 8af0151 commit 7118fcf

File tree

3 files changed

+89
-24
lines changed

3 files changed

+89
-24
lines changed

spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,9 @@
2828
import org.aopalliance.intercept.MethodInvocation;
2929
import org.hibernate.validator.HibernateValidator;
3030

31+
import org.springframework.core.BridgeMethodResolver;
3132
import org.springframework.core.annotation.AnnotationUtils;
33+
import org.springframework.util.ClassUtils;
3234
import org.springframework.util.ReflectionUtils;
3335
import org.springframework.validation.annotation.Validated;
3436

@@ -111,24 +113,42 @@ public MethodValidationInterceptor(Validator validator) {
111113
@SuppressWarnings("unchecked")
112114
public Object invoke(MethodInvocation invocation) throws Throwable {
113115
Class<?>[] groups = determineValidationGroups(invocation);
116+
114117
if (forExecutablesMethod != null) {
115-
Object executableValidator = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator);
116-
Set<ConstraintViolation<?>> result = (Set<ConstraintViolation<?>>)
117-
ReflectionUtils.invokeMethod(validateParametersMethod, executableValidator,
118-
invocation.getThis(), invocation.getMethod(), invocation.getArguments(), groups);
118+
// Standard Bean Validation 1.1 API
119+
Object execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator);
120+
Method methodToValidate = invocation.getMethod();
121+
Set<ConstraintViolation<?>> result;
122+
123+
try {
124+
result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod,
125+
execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
126+
}
127+
catch (IllegalArgumentException ex) {
128+
// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
129+
// Let's try to find the bridged method on the implementation class...
130+
methodToValidate = BridgeMethodResolver.findBridgedMethod(
131+
ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
132+
result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod,
133+
execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
134+
}
119135
if (!result.isEmpty()) {
120136
throw new ConstraintViolationException(result);
121137
}
138+
122139
Object returnValue = invocation.proceed();
123-
result = (Set<ConstraintViolation<?>>)
124-
ReflectionUtils.invokeMethod(validateReturnValueMethod, executableValidator,
125-
invocation.getThis(), invocation.getMethod(), returnValue, groups);
140+
141+
result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateReturnValueMethod,
142+
execVal, invocation.getThis(), methodToValidate, returnValue, groups);
126143
if (!result.isEmpty()) {
127144
throw new ConstraintViolationException(result);
128145
}
146+
129147
return returnValue;
130148
}
149+
131150
else {
151+
// Hibernate Validator 4.3's native API
132152
return HibernateValidatorDelegate.invokeWithinValidation(invocation, this.validator, groups);
133153
}
134154
}
@@ -179,4 +199,5 @@ public static Object invokeWithinValidation(MethodInvocation invocation, Validat
179199
return returnValue;
180200
}
181201
}
202+
182203
}

spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,28 +61,29 @@ public void testMethodValidationPostProcessor() {
6161
ac.registerSingleton("bean", MyValidBean.class);
6262
ac.refresh();
6363
doTestProxyValidation(ac.getBean("bean", MyValidInterface.class));
64+
ac.close();
6465
}
6566

6667

6768
private void doTestProxyValidation(MyValidInterface proxy) {
6869
assertNotNull(proxy.myValidMethod("value", 5));
6970
try {
7071
assertNotNull(proxy.myValidMethod("value", 15));
71-
fail("Should have thrown MethodConstraintViolationException");
72+
fail("Should have thrown ValidationException");
7273
}
7374
catch (javax.validation.ValidationException ex) {
7475
// expected
7576
}
7677
try {
7778
assertNotNull(proxy.myValidMethod(null, 5));
78-
fail("Should have thrown MethodConstraintViolationException");
79+
fail("Should have thrown ValidationException");
7980
}
8081
catch (javax.validation.ValidationException ex) {
8182
// expected
8283
}
8384
try {
8485
assertNotNull(proxy.myValidMethod("value", 0));
85-
fail("Should have thrown MethodConstraintViolationException");
86+
fail("Should have thrown ValidationException");
8687
}
8788
catch (javax.validation.ValidationException ex) {
8889
// expected
@@ -91,14 +92,23 @@ private void doTestProxyValidation(MyValidInterface proxy) {
9192
proxy.myValidAsyncMethod("value", 5);
9293
try {
9394
proxy.myValidAsyncMethod("value", 15);
94-
fail("Should have thrown MethodConstraintViolationException");
95+
fail("Should have thrown ValidationException");
9596
}
9697
catch (javax.validation.ValidationException ex) {
9798
// expected
9899
}
99100
try {
100101
proxy.myValidAsyncMethod(null, 5);
101-
fail("Should have thrown MethodConstraintViolationException");
102+
fail("Should have thrown ValidationException");
103+
}
104+
catch (javax.validation.ValidationException ex) {
105+
// expected
106+
}
107+
108+
assertEquals("myValue", proxy.myGenericMethod("myValue"));
109+
try {
110+
proxy.myGenericMethod(null);
111+
fail("Should have thrown ValidationException");
102112
}
103113
catch (javax.validation.ValidationException ex) {
104114
// expected
@@ -107,7 +117,7 @@ private void doTestProxyValidation(MyValidInterface proxy) {
107117

108118

109119
@MyStereotype
110-
public static class MyValidBean implements MyValidInterface {
120+
public static class MyValidBean implements MyValidInterface<String> {
111121

112122
@Override
113123
public Object myValidMethod(String arg1, int arg2) {
@@ -117,15 +127,22 @@ public Object myValidMethod(String arg1, int arg2) {
117127
@Override
118128
public void myValidAsyncMethod(String arg1, int arg2) {
119129
}
130+
131+
@Override
132+
public String myGenericMethod(String value) {
133+
return value;
134+
}
120135
}
121136

122137

123-
public interface MyValidInterface {
138+
public interface MyValidInterface<T> {
124139

125140
@NotNull Object myValidMethod(@NotNull(groups = MyGroup.class) String arg1, @Max(10) int arg2);
126141

127142
@MyValid
128143
@Async void myValidAsyncMethod(@NotNull(groups = OtherGroup.class) String arg1, @Max(10) int arg2);
144+
145+
T myGenericMethod(@NotNull T value);
129146
}
130147

131148

spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,21 @@ private void doTestProxyValidation(MyValidInterface proxy) {
7272
assertNotNull(proxy.myValidMethod("value", 5));
7373
try {
7474
assertNotNull(proxy.myValidMethod("value", 15));
75-
fail("Should have thrown MethodConstraintViolationException");
75+
fail("Should have thrown ValidationException");
7676
}
7777
catch (javax.validation.ValidationException ex) {
7878
// expected
7979
}
8080
try {
8181
assertNotNull(proxy.myValidMethod(null, 5));
82-
fail("Should have thrown MethodConstraintViolationException");
82+
fail("Should have thrown ValidationException");
8383
}
8484
catch (javax.validation.ValidationException ex) {
8585
// expected
8686
}
8787
try {
8888
assertNotNull(proxy.myValidMethod("value", 0));
89-
fail("Should have thrown MethodConstraintViolationException");
89+
fail("Should have thrown ValidationException");
9090
}
9191
catch (javax.validation.ValidationException ex) {
9292
// expected
@@ -95,14 +95,23 @@ private void doTestProxyValidation(MyValidInterface proxy) {
9595
proxy.myValidAsyncMethod("value", 5);
9696
try {
9797
proxy.myValidAsyncMethod("value", 15);
98-
fail("Should have thrown MethodConstraintViolationException");
98+
fail("Should have thrown ValidationException");
9999
}
100100
catch (javax.validation.ValidationException ex) {
101101
// expected
102102
}
103103
try {
104104
proxy.myValidAsyncMethod(null, 5);
105-
fail("Should have thrown MethodConstraintViolationException");
105+
fail("Should have thrown ValidationException");
106+
}
107+
catch (javax.validation.ValidationException ex) {
108+
// expected
109+
}
110+
111+
assertEquals("myValue", proxy.myGenericMethod("myValue"));
112+
try {
113+
proxy.myGenericMethod(null);
114+
fail("Should have thrown ValidationException");
106115
}
107116
catch (javax.validation.ValidationException ex) {
108117
// expected
@@ -111,7 +120,7 @@ private void doTestProxyValidation(MyValidInterface proxy) {
111120

112121

113122
@MyStereotype
114-
public static class MyValidBean implements MyValidInterface {
123+
public static class MyValidBean implements MyValidInterface<String> {
115124

116125
@Override
117126
public Object myValidMethod(String arg1, int arg2) {
@@ -121,24 +130,42 @@ public Object myValidMethod(String arg1, int arg2) {
121130
@Override
122131
public void myValidAsyncMethod(String arg1, int arg2) {
123132
}
133+
134+
@Override
135+
public String myGenericMethod(String value) {
136+
return value;
137+
}
124138
}
125139

126140

127-
public interface MyValidInterface {
141+
public interface MyValidInterface<T> {
128142

129143
@NotNull Object myValidMethod(@NotNull(groups = MyGroup.class) String arg1, @Max(10) int arg2);
130144

131-
@Async void myValidAsyncMethod(@NotNull(groups = MyGroup.class) String arg1, @Max(10) int arg2);
145+
@MyValid
146+
@Async void myValidAsyncMethod(@NotNull(groups = OtherGroup.class) String arg1, @Max(10) int arg2);
147+
148+
T myGenericMethod(@NotNull T value);
132149
}
133150

134151

135152
public interface MyGroup {
136153
}
137154

138155

156+
public interface OtherGroup {
157+
}
158+
159+
139160
@Validated({MyGroup.class, Default.class})
140161
@Retention(RetentionPolicy.RUNTIME)
141162
public @interface MyStereotype {
142163
}
143164

165+
166+
@Validated({OtherGroup.class, Default.class})
167+
@Retention(RetentionPolicy.RUNTIME)
168+
public @interface MyValid {
169+
}
170+
144171
}

0 commit comments

Comments
 (0)