Browse Source
Exclude `@ConfigurationProperties` beans from method validation so that `@Validated` can be used on final classes without the method validation post-processor throwing an exception. This commit introduces a `FilteredMethodValidationPostProcessor` class which will use `MethodValidationExcludeFilters` to exclude beans from method validation processing. Using `@EnableConfigurationProperties` will automatically register an appropriate filter. Closes gh-21454pull/28993/head
9 changed files with 288 additions and 7 deletions
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.boot.validation.beanvalidation; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.stream.Collectors; |
||||
import java.util.stream.Stream; |
||||
|
||||
import org.springframework.aop.ClassFilter; |
||||
import org.springframework.aop.MethodMatcher; |
||||
import org.springframework.aop.support.ComposablePointcut; |
||||
import org.springframework.aop.support.DefaultPointcutAdvisor; |
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; |
||||
|
||||
/** |
||||
* Custom {@link MethodValidationPostProcessor} that applies |
||||
* {@code MethodValidationExclusionFilter exclusion filters}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 2.4.0 |
||||
*/ |
||||
public class FilteredMethodValidationPostProcessor extends MethodValidationPostProcessor { |
||||
|
||||
private final Collection<MethodValidationExcludeFilter> excludeFilters; |
||||
|
||||
/** |
||||
* Creates a new {@code ExcludingMethodValidationPostProcessor} that will apply the |
||||
* given {@code exclusionFilters} when identifying beans that are eligible for method |
||||
* validation post-processing. |
||||
* @param excludeFilters filters to apply |
||||
*/ |
||||
public FilteredMethodValidationPostProcessor(Stream<? extends MethodValidationExcludeFilter> excludeFilters) { |
||||
this.excludeFilters = excludeFilters.collect(Collectors.toList()); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code ExcludingMethodValidationPostProcessor} that will apply the |
||||
* given {@code exclusionFilters} when identifying beans that are eligible for method |
||||
* validation post-processing. |
||||
* @param excludeFilters filters to apply |
||||
*/ |
||||
public FilteredMethodValidationPostProcessor(Collection<? extends MethodValidationExcludeFilter> excludeFilters) { |
||||
this.excludeFilters = new ArrayList<>(excludeFilters); |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() { |
||||
super.afterPropertiesSet(); |
||||
DefaultPointcutAdvisor advisor = (DefaultPointcutAdvisor) this.advisor; |
||||
ClassFilter classFilter = advisor.getPointcut().getClassFilter(); |
||||
MethodMatcher methodMatcher = advisor.getPointcut().getMethodMatcher(); |
||||
advisor.setPointcut(new ComposablePointcut(classFilter, methodMatcher).intersection(this::isIncluded)); |
||||
} |
||||
|
||||
private boolean isIncluded(Class<?> candidate) { |
||||
for (MethodValidationExcludeFilter exclusionFilter : this.excludeFilters) { |
||||
if (exclusionFilter.isExcluded(candidate)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.boot.validation.beanvalidation; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
|
||||
import org.springframework.core.annotation.MergedAnnotations; |
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; |
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; |
||||
|
||||
/** |
||||
* A filter for excluding types from method validation. |
||||
* |
||||
* @author Andy Wilkinson |
||||
* @since 2.4.0 |
||||
* @see MethodValidationPostProcessor |
||||
*/ |
||||
public interface MethodValidationExcludeFilter { |
||||
|
||||
/** |
||||
* Evaluate whether to exclude the given {@code type} from method validation. |
||||
* @param type the type to evaluate |
||||
* @return {@code true} to exclude the type from method validation, otherwise |
||||
* {@code false}. |
||||
*/ |
||||
boolean isExcluded(Class<?> type); |
||||
|
||||
/** |
||||
* Factory method to crate a {@link MethodValidationExcludeFilter} that excludes |
||||
* classes by annotation. |
||||
* @param annotationType the annotation to check |
||||
* @return a {@link MethodValidationExcludeFilter} instance |
||||
*/ |
||||
static MethodValidationExcludeFilter byAnnotation(Class<? extends Annotation> annotationType) { |
||||
return byAnnotation(annotationType, SearchStrategy.INHERITED_ANNOTATIONS); |
||||
} |
||||
|
||||
/** |
||||
* Factory method to crate a {@link MethodValidationExcludeFilter} that excludes |
||||
* classes by annotation. |
||||
* @param annotationType the annotation to check |
||||
* @param searchStrategy the annotation search strategy |
||||
* @return a {@link MethodValidationExcludeFilter} instance |
||||
*/ |
||||
static MethodValidationExcludeFilter byAnnotation(Class<? extends Annotation> annotationType, |
||||
SearchStrategy searchStrategy) { |
||||
return (type) -> MergedAnnotations.from(type, SearchStrategy.SUPERCLASS).isPresent(annotationType); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* Copyright 2012-2020 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. |
||||
*/ |
||||
|
||||
/** |
||||
* Utilities and classes related to bean validation. |
||||
*/ |
||||
package org.springframework.boot.validation.beanvalidation; |
||||
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
/* |
||||
* Copyright 2012-2020 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.boot.validation.beanvalidation; |
||||
|
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link MethodValidationExcludeFilter}. |
||||
* |
||||
* @author Andy Wilkinson |
||||
*/ |
||||
class MethodValidationExcludeFilterTests { |
||||
|
||||
@Test |
||||
void byAnnotationWhenClassIsAnnotatedExcludes() { |
||||
MethodValidationExcludeFilter filter = MethodValidationExcludeFilter.byAnnotation(Indicator.class); |
||||
assertThat(filter.isExcluded(Annotated.class)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
void byAnnotationWhenClassIsNotAnnotatedIncludes() { |
||||
MethodValidationExcludeFilter filter = MethodValidationExcludeFilter.byAnnotation(Indicator.class); |
||||
assertThat(filter.isExcluded(Plain.class)).isFalse(); |
||||
} |
||||
|
||||
static class Plain { |
||||
|
||||
} |
||||
|
||||
@Indicator |
||||
static class Annotated { |
||||
|
||||
} |
||||
|
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@interface Indicator { |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue