Skip to content

Commit a8ea472

Browse files
committed
Refactoring in MethodValidationResult
Remove throwIfViolationsPresent and replace with static factory methods on MethodValidationException taking MethodValidationResult, which makes handling more explicit and allows choice of what exception to raise. Update MethodValidationResult to expose the target, the method, and forReturnValue flag, so the code handling an exception will have access to all details. See gh-30644
1 parent 1393fac commit a8ea472

File tree

6 files changed

+122
-110
lines changed

6 files changed

+122
-110
lines changed

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

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,47 +46,53 @@ public Class<?>[] determineValidationGroups(Object bean, Method method) {
4646

4747
@Override
4848
public void validateArguments(
49-
Object target, Method method, @Nullable MethodParameter[] parameters, Object[] arguments,
50-
Class<?>[] groups) {
49+
Object target, Method method, @Nullable MethodParameter[] parameters,
50+
Object[] arguments, Class<?>[] groups) {
5151

52-
handleArgumentsValidationResult(target, method, arguments, groups,
53-
this.adapter.validateMethodArguments(target, method, parameters, arguments, groups));
54-
}
55-
56-
public void validateReturnValue(
57-
Object target, Method method, @Nullable MethodParameter returnType, @Nullable Object returnValue,
58-
Class<?>[] groups) {
52+
MethodValidationResult validationResult =
53+
this.adapter.validateMethodArguments(target, method, parameters, arguments, groups);
5954

60-
handleReturnValueValidationResult(target, method, returnValue, groups,
61-
this.adapter.validateMethodReturnValue(target, method, returnType, returnValue, groups));
55+
handleArgumentsResult(arguments, groups, validationResult);
6256
}
6357

6458
/**
6559
* Subclasses can override this to handle the result of argument validation.
66-
* By default, {@link MethodValidationResult#throwIfViolationsPresent()} is called.
67-
* @param bean the target Object for method invocation
68-
* @param method the target method
60+
* By default, throws {@link MethodValidationException} if there are errors.
6961
* @param arguments the candidate argument values to validate
7062
* @param groups groups for validation determined via
63+
* @param validationResult the result from validation
7164
*/
72-
protected void handleArgumentsValidationResult(
73-
Object bean, Method method, Object[] arguments, Class<?>[] groups, MethodValidationResult result) {
65+
protected void handleArgumentsResult(
66+
Object[] arguments, Class<?>[] groups, MethodValidationResult validationResult) {
67+
68+
if (validationResult.hasViolations()) {
69+
throw MethodValidationException.forResult(validationResult);
70+
}
71+
}
72+
73+
public void validateReturnValue(
74+
Object target, Method method, @Nullable MethodParameter returnType,
75+
@Nullable Object returnValue, Class<?>[] groups) {
76+
77+
MethodValidationResult validationResult =
78+
this.adapter.validateMethodReturnValue(target, method, returnType, returnValue, groups);
7479

75-
result.throwIfViolationsPresent();
80+
handleReturnValueResult(returnValue, groups, validationResult);
7681
}
7782

7883
/**
7984
* Subclasses can override this to handle the result of return value validation.
80-
* By default, {@link MethodValidationResult#throwIfViolationsPresent()} is called.
81-
* @param bean the target Object for method invocation
82-
* @param method the target method
85+
* By default, throws {@link MethodValidationException} if there are errors.
8386
* @param returnValue the return value to validate
8487
* @param groups groups for validation determined via
88+
* @param validationResult the result from validation
8589
*/
86-
protected void handleReturnValueValidationResult(
87-
Object bean, Method method, @Nullable Object returnValue, Class<?>[] groups, MethodValidationResult result) {
90+
protected void handleReturnValueResult(
91+
@Nullable Object returnValue, Class<?>[] groups, MethodValidationResult validationResult) {
8892

89-
result.throwIfViolationsPresent();
93+
if (validationResult.hasViolations()) {
94+
throw MethodValidationException.forResult(validationResult);
95+
}
9096
}
9197

9298
}

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

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.lang.reflect.Method;
2020
import java.util.ArrayList;
21-
import java.util.Collections;
2221
import java.util.Comparator;
2322
import java.util.Iterator;
2423
import java.util.LinkedHashMap;
@@ -74,8 +73,6 @@ public class MethodValidationAdapter {
7473

7574
private static final Comparator<ParameterValidationResult> RESULT_COMPARATOR = new ResultComparator();
7675

77-
private static final MethodValidationResult EMPTY_RESULT = new EmptyMethodValidationResult();
78-
7976

8077
private final Supplier<Validator> validator;
8178

@@ -232,7 +229,8 @@ public MethodValidationResult validateMethodArguments(
232229
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(mostSpecificMethod);
233230
result = execVal.validateParameters(target, bridgedMethod, arguments, groups);
234231
}
235-
return (result.isEmpty() ? EMPTY_RESULT :
232+
return (result.isEmpty() ?
233+
MethodValidationException.forEmptyResult(target, method, true) :
236234
createException(target, method, result,
237235
i -> parameters != null ? parameters[i] : new MethodParameter(method, i),
238236
i -> arguments[i],
@@ -257,7 +255,8 @@ public MethodValidationResult validateMethodReturnValue(
257255

258256
ExecutableValidator execVal = this.validator.get().forExecutables();
259257
Set<ConstraintViolation<Object>> result = execVal.validateReturnValue(target, method, returnValue, groups);
260-
return (result.isEmpty() ? EMPTY_RESULT :
258+
return (result.isEmpty() ?
259+
MethodValidationException.forEmptyResult(target, method, true) :
261260
createException(target, method, result,
262261
i -> returnType != null ? returnType : new MethodParameter(method, -1),
263262
i -> returnValue,
@@ -310,7 +309,7 @@ else if (node.getKind().equals(ElementKind.RETURN_VALUE)) {
310309
cascadedViolations.forEach((node, builder) -> validatonResultList.add(builder.build()));
311310
validatonResultList.sort(RESULT_COMPARATOR);
312311

313-
return new MethodValidationException(target, method, violations, validatonResultList, forReturnValue);
312+
return new MethodValidationException(target, method, forReturnValue, violations, validatonResultList);
314313
}
315314

316315
/**
@@ -533,41 +532,4 @@ private <E> int compareKeys(ParameterErrors errors1, ParameterErrors errors2) {
533532
}
534533
}
535534

536-
537-
/**
538-
* {@link MethodValidationResult} to use when there are no violations.
539-
*/
540-
private static final class EmptyMethodValidationResult implements MethodValidationResult {
541-
542-
@Override
543-
public Set<ConstraintViolation<?>> getConstraintViolations() {
544-
return Collections.emptySet();
545-
}
546-
547-
@Override
548-
public List<ParameterValidationResult> getAllValidationResults() {
549-
return Collections.emptyList();
550-
}
551-
552-
@Override
553-
public List<ParameterValidationResult> getValueResults() {
554-
return Collections.emptyList();
555-
}
556-
557-
@Override
558-
public List<ParameterErrors> getBeanResults() {
559-
return Collections.emptyList();
560-
}
561-
562-
@Override
563-
public void throwIfViolationsPresent() {
564-
}
565-
566-
@Override
567-
public String toString() {
568-
return "MethodValidationResult (0 violations)";
569-
}
570-
571-
}
572-
573535
}

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

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.validation.beanvalidation;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.Collections;
2021
import java.util.List;
2122
import java.util.Set;
2223

@@ -32,7 +33,6 @@
3233
* {@link org.springframework.context.MessageSourceResolvable} and grouped by
3334
* method parameter.
3435
*
35-
*
3636
* @author Rossen Stoyanchev
3737
* @since 6.1
3838
* @see ParameterValidationResult
@@ -51,49 +51,55 @@ public class MethodValidationException extends ConstraintViolationException impl
5151
private final boolean forReturnValue;
5252

5353

54-
public MethodValidationException(
55-
Object target, Method method, Set<? extends ConstraintViolation<?>> violations,
56-
List<ParameterValidationResult> validationResults, boolean forReturnValue) {
54+
/**
55+
* Package private constructor for {@link MethodValidationAdapter}.
56+
*/
57+
MethodValidationException(
58+
Object target, Method method, boolean forReturnValue,
59+
Set<? extends ConstraintViolation<?>> violations, List<ParameterValidationResult> results) {
5760

5861
super(violations);
59-
Assert.notEmpty(violations, "'violations' must not be empty");
62+
63+
Assert.notNull(violations, "'violations' is required");
64+
Assert.notNull(results, "'results' is required");
65+
6066
this.target = target;
6167
this.method = method;
62-
this.allValidationResults = validationResults;
68+
this.allValidationResults = results;
6369
this.forReturnValue = forReturnValue;
6470
}
6571

66-
6772
/**
68-
* Return the target of the method invocation to which validation was applied.
73+
* Private constructor copying from another {@code MethodValidationResult}.
6974
*/
75+
private MethodValidationException(MethodValidationResult other) {
76+
this(other.getTarget(), other.getMethod(), other.isForReturnValue(),
77+
other.getConstraintViolations(), other.getAllValidationResults());
78+
}
79+
80+
81+
// re-declare getConstraintViolations as NonNull
82+
83+
@Override
84+
public Set<ConstraintViolation<?>> getConstraintViolations() {
85+
return super.getConstraintViolations();
86+
}
87+
88+
@Override
7089
public Object getTarget() {
7190
return this.target;
7291
}
7392

74-
/**
75-
* Return the method to which validation was applied.
76-
*/
93+
@Override
7794
public Method getMethod() {
7895
return this.method;
7996
}
8097

81-
/**
82-
* Whether the violations are for a return value.
83-
* If true the violations are from validating a return value.
84-
* If false the violations are from validating method arguments.
85-
*/
98+
@Override
8699
public boolean isForReturnValue() {
87100
return this.forReturnValue;
88101
}
89102

90-
// re-declare parent class method for NonNull treatment of interface
91-
92-
@Override
93-
public Set<ConstraintViolation<?>> getConstraintViolations() {
94-
return super.getConstraintViolations();
95-
}
96-
97103
@Override
98104
public List<ParameterValidationResult> getAllValidationResults() {
99105
return this.allValidationResults;
@@ -114,15 +120,28 @@ public List<ParameterErrors> getBeanResults() {
114120
.toList();
115121
}
116122

117-
@Override
118-
public void throwIfViolationsPresent() {
119-
throw this;
120-
}
121-
122123
@Override
123124
public String toString() {
124125
return "MethodValidationResult (" + getConstraintViolations().size() + " violations) " +
125126
"for " + this.method.toGenericString();
126127
}
127128

129+
130+
/**
131+
* Create an exception copying from the given result, or return the same
132+
* instance if it is a {@code MethodValidationException} already.
133+
*/
134+
public static MethodValidationException forResult(MethodValidationResult result) {
135+
return (result instanceof MethodValidationException ex ? ex : new MethodValidationException(result));
136+
}
137+
138+
/**
139+
* Create an exception for validation without errors.
140+
*/
141+
public static MethodValidationException forEmptyResult(Object target, Method method, boolean forReturnValue) {
142+
return new MethodValidationException(
143+
target, method, forReturnValue, Collections.emptySet(), Collections.emptyList());
144+
}
145+
146+
128147
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,19 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
104104
Method method = invocation.getMethod();
105105
Class<?>[] groups = determineValidationGroups(invocation);
106106

107-
this.delegate.validateMethodArguments(target, method, null, invocation.getArguments(), groups)
108-
.throwIfViolationsPresent();
107+
MethodValidationResult result;
108+
109+
result = this.delegate.validateMethodArguments(target, method, null, invocation.getArguments(), groups);
110+
if (result.hasViolations()) {
111+
throw MethodValidationException.forResult(result);
112+
}
109113

110114
Object returnValue = invocation.proceed();
111115

112-
this.delegate.validateMethodReturnValue(target, method, null, returnValue, groups)
113-
.throwIfViolationsPresent();
116+
result = this.delegate.validateMethodReturnValue(target, method, null, returnValue, groups);
117+
if (result.hasViolations()) {
118+
throw MethodValidationException.forResult(result);
119+
}
114120

115121
return returnValue;
116122
}

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.validation.beanvalidation;
1818

19+
import java.lang.reflect.Method;
1920
import java.util.List;
2021
import java.util.Set;
2122

@@ -39,6 +40,30 @@
3940
*/
4041
public interface MethodValidationResult {
4142

43+
/**
44+
* Return the target of the method invocation to which validation was applied.
45+
*/
46+
Object getTarget();
47+
48+
/**
49+
* Return the method to which validation was applied.
50+
*/
51+
Method getMethod();
52+
53+
/**
54+
* Whether the violations are for a return value.
55+
* If true the violations are from validating a return value.
56+
* If false the violations are from validating method arguments.
57+
*/
58+
boolean isForReturnValue();
59+
60+
/**
61+
* Whether the result contains any {@link ConstraintViolation}s.
62+
*/
63+
default boolean hasViolations() {
64+
return !getConstraintViolations().isEmpty();
65+
}
66+
4267
/**
4368
* Returns the set of constraint violations reported during a validation.
4469
* @return the {@code Set} of {@link ConstraintViolation}s, or an empty Set
@@ -72,11 +97,4 @@ public interface MethodValidationResult {
7297
*/
7398
List<ParameterErrors> getBeanResults();
7499

75-
/**
76-
* Check if {@link #getConstraintViolations()} is empty, and if not, raise
77-
* {@link MethodValidationException}.
78-
* @throws MethodValidationException if the result contains any violations
79-
*/
80-
void throwIfViolationsPresent();
81-
82100
}

0 commit comments

Comments
 (0)