From f65b83f65ea8dab8b5b1432c5acdeda6d7052e6d Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Wed, 18 Jan 2023 16:06:50 +0000 Subject: [PATCH] Optimize performance of method validation Perform the check for whether validation for a HandlerMethod is needed earlier and only once rather than in the constructor of DataFetcherHandlerMethod. Make the validation helper passed to DataFetcherHandlerMethod stateful, so that validation groups are also determined once on startup. See gh-571 --- .../AnnotatedControllerConfigurer.java | 21 +-- .../support/DataFetcherHandlerMethod.java | 29 +--- .../HandlerMethodValidationHelper.java | 146 ---------------- .../annotation/support/ValidationHelper.java | 156 ++++++++++++++++++ ...rTests.java => ValidationHelperTests.java} | 90 ++++------ 5 files changed, 208 insertions(+), 234 deletions(-) delete mode 100644 spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelper.java create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java rename spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/{HandlerMethodValidationHelperTests.java => ValidationHelperTests.java} (58%) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java index 051db7f4..da700d77 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -125,7 +125,7 @@ public class AnnotatedControllerConfigurer private HandlerMethodArgumentResolverComposite argumentResolvers; @Nullable - private HandlerMethodValidationHelper validationHelper; + private ValidationHelper validationHelper; /** @@ -176,8 +176,7 @@ public class AnnotatedControllerConfigurer this.argumentResolvers = initArgumentResolvers(); if (beanValidationPresent) { - this.validationHelper = - HandlerMethodValidationHelper.createIfValidatorAvailable(obtainApplicationContext()); + this.validationHelper = ValidationHelper.createIfValidatorPresent(obtainApplicationContext()); } } @@ -228,7 +227,8 @@ public class AnnotatedControllerConfigurer findHandlerMethods().forEach((info) -> { DataFetcher dataFetcher; if (!info.isBatchMapping()) { - dataFetcher = new SchemaMappingDataFetcher(info, this.argumentResolvers, this.validationHelper, this.executor); + dataFetcher = new SchemaMappingDataFetcher( + info, this.argumentResolvers, this.validationHelper, this.executor); } else { String dataLoaderKey = registerBatchLoader(info); @@ -492,7 +492,7 @@ public class AnnotatedControllerConfigurer private final HandlerMethodArgumentResolverComposite argumentResolvers; @Nullable - private final HandlerMethodValidationHelper validatorHelper; + private final Consumer methodValidationHelper; @Nullable private final Executor executor; @@ -501,12 +501,12 @@ public class AnnotatedControllerConfigurer SchemaMappingDataFetcher( MappingInfo info, HandlerMethodArgumentResolverComposite resolvers, - @Nullable HandlerMethodValidationHelper validatorHelper, - @Nullable Executor executor) { + @Nullable ValidationHelper validationHelper, @Nullable Executor executor) { this.info = info; this.argumentResolvers = resolvers; - this.validatorHelper = validatorHelper; + this.methodValidationHelper = (validationHelper != null ? + validationHelper.getValidationHelperFor(info.getHandlerMethod()) : null); this.executor = executor; this.subscription = this.info.getCoordinates().getTypeName().equalsIgnoreCase("Subscription"); } @@ -524,7 +524,8 @@ public class AnnotatedControllerConfigurer public Object get(DataFetchingEnvironment environment) throws Exception { DataFetcherHandlerMethod handlerMethod = new DataFetcherHandlerMethod( - getHandlerMethod(), this.argumentResolvers, this.validatorHelper, this.executor, this.subscription); + getHandlerMethod(), this.argumentResolvers, this.methodValidationHelper, + this.executor, this.subscription); return handlerMethod.invoke(environment); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java index e33d9635..6795e842 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -17,6 +17,7 @@ package org.springframework.graphql.data.method.annotation.support; import java.util.Arrays; import java.util.concurrent.Executor; +import java.util.function.Consumer; import graphql.schema.DataFetchingEnvironment; import org.reactivestreams.Publisher; @@ -49,8 +50,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport { private final HandlerMethodArgumentResolverComposite resolvers; - @Nullable - private final HandlerMethodValidationHelper validator; + private final Consumer validationHelper; private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); @@ -61,17 +61,17 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport { * Constructor with a parent handler method. * @param handlerMethod the handler method * @param resolvers the argument resolvers - * @param validator the input validator + * @param validationHelper to apply bean validation with * @param subscription whether the field being fetched is of subscription type */ - public DataFetcherHandlerMethod(HandlerMethod handlerMethod, - HandlerMethodArgumentResolverComposite resolvers, @Nullable HandlerMethodValidationHelper validator, - @Nullable Executor executor, boolean subscription) { + public DataFetcherHandlerMethod( + HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers, + @Nullable Consumer validationHelper, @Nullable Executor executor, boolean subscription) { super(handlerMethod, executor); Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers"); this.resolvers = resolvers; - this.validator = (validator != null && validator.requiresValidation(handlerMethod) ? validator : null); + this.validationHelper = (validationHelper != null ? validationHelper : args -> {}); this.subscription = subscription; } @@ -83,15 +83,6 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport { return this.resolvers; } - /** - * Return the configured input validator. - * @deprecated as of 1.1 without a replacement - */ - @Deprecated - @Nullable - public HandlerMethodValidationHelper getValidator() { - return this.validator; - } /** @@ -188,9 +179,7 @@ public class DataFetcherHandlerMethod extends InvocableHandlerMethodSupport { @Nullable private Object validateAndInvoke(Object[] args, DataFetchingEnvironment environment) { - if (this.validator != null) { - this.validator.validate(this, args); - } + this.validationHelper.accept(args); return doInvoke(environment.getGraphQlContext(), args); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelper.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelper.java deleted file mode 100644 index d34d7a46..00000000 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelper.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2020-2022 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.graphql.data.method.annotation.support; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.validation.Constraint; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.ConstraintViolationException; -import jakarta.validation.Valid; -import jakarta.validation.Validator; -import jakarta.validation.metadata.BeanDescriptor; - -import org.springframework.context.ApplicationContext; -import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.graphql.data.method.HandlerMethod; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.validation.annotation.Validated; -import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; -import org.springframework.validation.beanvalidation.SpringValidatorAdapter; - -/** - * Helper class to apply standard bean validation to a {@link HandlerMethod}. - * - * @author Brian Clozel - * @author Rossen Stoyanchev - * @since 1.0 - */ -final class HandlerMethodValidationHelper { - - private final Validator validator; - - - /** - * Constructor with the {@link Validator} instance to use. - */ - public HandlerMethodValidationHelper(Validator validator) { - Assert.notNull(validator, "validator should not be null"); - if (validator instanceof LocalValidatorFactoryBean) { - this.validator = ((LocalValidatorFactoryBean) validator).getValidator(); - } - else if (validator instanceof SpringValidatorAdapter) { - this.validator = validator.unwrap(Validator.class); - } - else { - this.validator = validator; - } - } - - - /** - * Validate the input values to a the {@link HandlerMethod} and throw a - * {@link ConstraintViolationException} in case of violations. - * @param handlerMethod the handler method to validate - * @param arguments the input argument values - */ - public void validate(HandlerMethod handlerMethod, Object[] arguments) { - Set> result = - this.validator.forExecutables().validateParameters( - handlerMethod.getBean(), handlerMethod.getMethod(), arguments, - determineValidationGroups(handlerMethod)); - if (!result.isEmpty()) { - throw new ConstraintViolationException(result); - } - } - - /** - * Determine the validation groups to apply to a handler method, specified - * through the {@link Validated} annotation on the method or on the class. - * @param method the method to check - * @return the applicable validation groups as a Class array - */ - private Class[] determineValidationGroups(HandlerMethod method) { - Validated annotation = findAnnotation(method, Validated.class); - return (annotation != null ? annotation.value() : new Class[0]); - } - - @Nullable - private static A findAnnotation(HandlerMethod method, Class annotationType) { - A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType); - if (annotation == null) { - annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType); - } - return annotation; - } - - /** - * Whether the given method requires standard bean validation. This is the - * case if the method or one of its parameters are annotated with - * {@link Valid} or {@link Validated}, or if any method parameter is declared - * with a {@link Constraint constraint}, or the method parameter type is - * itself {@link BeanDescriptor#isBeanConstrained() constrained}. - * @param method the handler method to check - * @return {@code true} if validation is required, {@code false} otherwise - */ - public boolean requiresValidation(HandlerMethod method) { - if (findAnnotation(method, Validated.class) != null || findAnnotation(method, Valid.class) != null) { - return true; - } - for (MethodParameter parameter : method.getMethodParameters()) { - for (Annotation annotation : parameter.getParameterAnnotations()) { - MergedAnnotations merged = MergedAnnotations.from(annotation); - if (merged.isPresent(Valid.class) || merged.isPresent(Constraint.class) || merged.isPresent(Validated.class)) { - return true; - } - } - Class paramType = parameter.nestedIfOptional().getNestedParameterType(); - if (this.validator.getConstraintsForClass(paramType).isBeanConstrained()) { - return true; - } - } - return false; - } - - - /** - * Factory method for {@link HandlerMethodValidationHelper} if a - * {@link Validator} can be found. - * @param context the context to look up a {@code Validator} bean from - * @return the helper instance, or {@code null - */ - @Nullable - public static HandlerMethodValidationHelper createIfValidatorAvailable(ApplicationContext context) { - Validator validator = context.getBeanProvider(Validator.class).getIfAvailable(); - return (validator != null ? new HandlerMethodValidationHelper(validator) : null); - } - -} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java new file mode 100644 index 00000000..31cea7e3 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ValidationHelper.java @@ -0,0 +1,156 @@ +/* + * Copyright 2002-2023 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.graphql.data.method.annotation.support; + +import java.lang.annotation.Annotation; +import java.util.Set; +import java.util.function.Consumer; + +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Valid; +import jakarta.validation.Validator; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.graphql.data.method.HandlerMethod; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.validation.beanvalidation.SpringValidatorAdapter; + +/** + * Holds a {@link jakarta.validation.Validator} and helps to create a validation + * callback for a given {@link HandlerMethod} if it is determined that it + * requires bean validation. + * + * @author Rossen Stoyanchev + * @since 1.2 + */ +class ValidationHelper { + + private final Validator validator; + + + private ValidationHelper(Validator validator) { + Assert.notNull(validator, "Validator is required"); + this.validator = validator; + } + + + /** + * Create a validation callback for the given {@link HandlerMethod}, + * possibly {@code null} if the method or the method parameters do not have + * {@link Validated}, {@link Valid}, or {@link Constraint} annotations. + */ + @Nullable + public Consumer getValidationHelperFor(HandlerMethod handlerMethod) { + + boolean required = false; + Class[] groups = null; + + Validated validatedAnnotation = findAnnotation(handlerMethod, Validated.class); + if (validatedAnnotation != null) { + required = true; + groups = validatedAnnotation.value(); + } + else if (findAnnotation(handlerMethod, Valid.class) != null) { + required = true; + } + + for (MethodParameter parameter : handlerMethod.getMethodParameters()) { + if (required) { + break; + } + for (Annotation annot : parameter.getParameterAnnotations()) { + MergedAnnotations merged = MergedAnnotations.from(annot); + if (merged.isPresent(Valid.class) || merged.isPresent(Constraint.class) || merged.isPresent(Validated.class)) { + required = true; + } + } + } + + return (required ? new HandlerMethodValidator(handlerMethod, groups) : null); + } + + @Nullable + private A findAnnotation(HandlerMethod method, Class annotationType) { + A annotation = AnnotationUtils.findAnnotation(method.getMethod(), annotationType); + if (annotation == null) { + annotation = AnnotationUtils.findAnnotation(method.getBeanType(), annotationType); + } + return annotation; + } + + + /** + * Factory method to create a {@link ValidationHelper} if there is a + * {@link Validator} bean declared, or {@code null} otherwise. + */ + @Nullable + public static ValidationHelper createIfValidatorPresent(ApplicationContext context) { + Validator validator = context.getBeanProvider(Validator.class).getIfAvailable(); + if (validator instanceof LocalValidatorFactoryBean) { + validator = ((LocalValidatorFactoryBean) validator).getValidator(); + } + else if (validator instanceof SpringValidatorAdapter) { + validator = validator.unwrap(Validator.class); + } + return (validator != null ? create(validator) : null); + } + + /** + * Factory method with a given {@link Validator} instance. + */ + public static ValidationHelper create(Validator validator) { + return new ValidationHelper(validator); + } + + + /** + * Callback to apply validation to the invocation of a {@link HandlerMethod}. + */ + private class HandlerMethodValidator implements Consumer { + + private final HandlerMethod handlerMethod; + + @Nullable + private final Class[] validationGroups; + + private HandlerMethodValidator(HandlerMethod handlerMethod, @Nullable Class[] validationGroups) { + Assert.notNull(handlerMethod, "HandlerMethod is required"); + this.handlerMethod = handlerMethod; + this.validationGroups = (validationGroups != null ? validationGroups : new Class[] {}); + } + + @Override + public void accept(Object[] arguments) { + + Set> result = + ValidationHelper.this.validator.forExecutables().validateParameters( + this.handlerMethod.getBean(), this.handlerMethod.getMethod(), arguments, this.validationGroups); + + if (!result.isEmpty()) { + throw new ConstraintViolationException(result); + } + } + } + +} diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelperTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ValidationHelperTests.java similarity index 58% rename from spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelperTests.java rename to spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ValidationHelperTests.java index 9bec8b52..195b59a5 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelperTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ValidationHelperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2023 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. @@ -20,7 +20,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.util.Arrays; -import java.util.Optional; +import java.util.function.Consumer; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; @@ -28,7 +28,6 @@ import jakarta.validation.Valid; import jakarta.validation.Validation; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.NotNull; - import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.IterableAssert; import org.assertj.core.api.ThrowableAssert; @@ -39,71 +38,63 @@ import org.springframework.graphql.data.method.HandlerMethod; import org.springframework.validation.annotation.Validated; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * Unit tests for {@link HandlerMethodValidationHelper}. + * Unit tests for {@link ValidationHelper}. + * * @author Brian Clozel */ -class HandlerMethodValidationHelperTests { - - private final HandlerMethodValidationHelper validator = - new HandlerMethodValidationHelper(Validation.buildDefaultValidatorFactory().getValidator()); - +class ValidationHelperTests { @Test void shouldFailWithNullValidator() { - assertThatThrownBy(() -> new HandlerMethodValidationHelper(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> ValidationHelper.create(null)).isInstanceOf(IllegalArgumentException.class); } @Test void shouldIgnoreMethodsWithoutAnnotations() { - HandlerMethod method = findHandlerMethod(MyBean.class, "notValidatedMethod"); - assertThatNoException().isThrownBy(() -> validator.validate(method, new Object[] {"test", 12})); + Consumer validator = createValidator(MyBean.class, "notValidatedMethod"); + assertThat(validator).isNull(); } @Test void shouldRaiseValidationErrorForAnnotatedParams() { - HandlerMethod method = findHandlerMethod(MyBean.class, "myValidMethod"); - assertViolations(() -> validator.validate(method, new Object[] {null, 2})) + Consumer validator = createValidator(MyBean.class, "myValidMethod"); + assertViolations(() -> validator.accept(new Object[] {null, 2})) .anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethod.arg0")); - assertViolations(() -> validator.validate(method, new Object[] {"test", 12})) + assertViolations(() -> validator.accept(new Object[] {"test", 12})) .anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethod.arg1")); } @Test void shouldRaiseValidationErrorForAnnotatedParamsWithGroups() { - HandlerMethod myValidMethodWithGroup = findHandlerMethod(MyValidationGroupsBean.class, "myValidMethodWithGroup"); - assertViolations(() -> validator.validate(myValidMethodWithGroup, new Object[] {null})) - .anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethodWithGroup.arg0")); + Consumer validator1 = createValidator(MyValidationGroupsBean.class, "myValidMethodWithGroup"); + assertViolation(() -> validator1.accept(new Object[] {null}), "myValidMethodWithGroup.arg0"); - HandlerMethod myValidMethodWithGroupOnType = findHandlerMethod(MyValidationGroupsBean.class, "myValidMethodWithGroupOnType"); - assertViolations(() -> validator.validate(myValidMethodWithGroupOnType, new Object[] {null})) - .anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethodWithGroupOnType.arg0")); + Consumer validator2 = createValidator(MyValidationGroupsBean.class, "myValidMethodWithGroupOnType"); + assertViolation(() -> validator2.accept(new Object[] {null}), "myValidMethodWithGroupOnType.arg0"); } @Test void shouldRecognizeMethodsThatRequireValidation() { - HandlerMethod method = findHandlerMethod(RequiresValidationBean.class, "processConstrainedValue"); - assertThat(validator.requiresValidation(method)).isTrue(); + Consumer validator1 = createValidator(RequiresValidationBean.class, "processConstrainedValue"); + assertThat(validator1).isNotNull(); - method = findHandlerMethod(RequiresValidationBean.class, "processValidInput"); - assertThat(validator.requiresValidation(method)).isTrue(); + Consumer validator2 = createValidator(RequiresValidationBean.class, "processValidInput"); + assertThat(validator2).isNotNull(); - method = findHandlerMethod(RequiresValidationBean.class, "processValidatedInput"); - assertThat(validator.requiresValidation(method)).isTrue(); + Consumer validator3 = createValidator(RequiresValidationBean.class, "processValidatedInput"); + assertThat(validator3).isNotNull(); - method = findHandlerMethod(RequiresValidationBean.class, "processInputWithConstrainedValue"); - assertThat(validator.requiresValidation(method)).isTrue(); - - method = findHandlerMethod(RequiresValidationBean.class, "processOptionalInputWithConstrainedValue"); - assertThat(validator.requiresValidation(method)).isTrue(); - - method = findHandlerMethod(RequiresValidationBean.class, "processValue"); - assertThat(validator.requiresValidation(method)).isFalse(); + Consumer validator4 = createValidator(RequiresValidationBean.class, "processValue"); + assertThat(validator4).isNull(); } + private Consumer createValidator(Class handlerType, String methodName) { + return ValidationHelper.create(Validation.buildDefaultValidatorFactory().getValidator()) + .getValidationHelperFor(findHandlerMethod(handlerType, methodName)); + } private HandlerMethod findHandlerMethod(Class handlerType, String methodName) { Object handler = BeanUtils.instantiateClass(handlerType); @@ -121,6 +112,11 @@ class HandlerMethodValidationHelperTests { .asInstanceOf(InstanceOfAssertFactories.iterable(ConstraintViolation.class)); } + private void assertViolation(ThrowableAssert.ThrowingCallable callable, String propertyPath) { + assertViolations(callable).anyMatch(violation -> + violation.getPropertyPath().toString().equals(propertyPath)); + } + @SuppressWarnings("unused") private static class MyBean { @@ -184,34 +180,12 @@ class HandlerMethodValidationHelperTests { public void processValidatedInput(@Validated MyInput input) { } - public void processInputWithConstrainedValue(MyConstrainedInput input) { - } - - public void processOptionalInputWithConstrainedValue(Optional input) { - } - public void processValue(int i) { } - } private static class MyInput { } - private static class MyConstrainedInput { - - @Max(99) - private int i; - - public int getI() { - return this.i; - } - - public void setI(int i) { - this.i = i; - } - - } - }