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