Exclude property beans from method validation
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
parent
a0862f9146
commit
f60f3cb38e
@ -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 @@
|
|||||||
|
/*
|
||||||
|
* 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 @@
|
|||||||
|
/*
|
||||||
|
* 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 @@
|
|||||||
|
/*
|
||||||
|
* 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