diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinderBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinderBuilder.java index 29e96535c17..57980d6145d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinderBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinderBuilder.java @@ -147,7 +147,7 @@ class ConfigurationPropertiesBinderBuilder { return defaultValidator; } if (isJsr303Present()) { - return new ValidatedLocalValidatorFactoryBean(this.applicationContext); + return new Jsr303ConfigurationPropertiesValidator(this.applicationContext); } return null; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/Jsr303ConfigurationPropertiesValidator.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/Jsr303ConfigurationPropertiesValidator.java new file mode 100644 index 00000000000..2582ec8d2e1 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/Jsr303ConfigurationPropertiesValidator.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.context.properties; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.springframework.boot.validation.MessageInterpolatorFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; +import org.springframework.validation.annotation.Validated; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +/** + * Validator that supports configuration classes annotated with + * {@link Validated @Validated}. + * + * @author Phillip Webb + */ +class Jsr303ConfigurationPropertiesValidator implements Validator { + + private final Future delegate; + + Jsr303ConfigurationPropertiesValidator(ApplicationContext applicationContext) { + // Creating the delegate is slow so we do it in the background + ExecutorService executor = Executors.newSingleThreadExecutor(); + this.delegate = executor.submit(() -> new Delegate(applicationContext)); + executor.shutdown(); + if (applicationContext instanceof ConfigurableApplicationContext) { + ((ConfigurableApplicationContext) applicationContext) + .addApplicationListener(new Listener()); + } + } + + @Override + public boolean supports(Class type) { + return AnnotatedElementUtils.hasAnnotation(type, Validated.class) + && getDelegate().supports(type); + } + + @Override + public void validate(Object target, Errors errors) { + getDelegate().validate(target, errors); + } + + private Delegate getDelegate() { + try { + return this.delegate.get(); + } + catch (InterruptedException ex) { + throw new IllegalStateException(ex); + } + catch (ExecutionException ex) { + ReflectionUtils.rethrowRuntimeException(ex.getCause()); + return null; + } + } + + private static class Delegate extends LocalValidatorFactoryBean { + + Delegate(ApplicationContext applicationContext) { + setApplicationContext(applicationContext); + setMessageInterpolator(new MessageInterpolatorFactory().getObject()); + afterPropertiesSet(); + } + + } + + private class Listener implements ApplicationListener { + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + getDelegate(); + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ValidatedLocalValidatorFactoryBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ValidatedLocalValidatorFactoryBean.java deleted file mode 100644 index a7501e4fcd6..00000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ValidatedLocalValidatorFactoryBean.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-2017 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 - * - * http://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.context.properties; - -import org.springframework.boot.validation.MessageInterpolatorFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.validation.annotation.Validated; -import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; - -/** - * {@link LocalValidatorFactoryBean} supports classes annotated with - * {@link Validated @Validated}. - * - * @author Phillip Webb - */ -class ValidatedLocalValidatorFactoryBean extends LocalValidatorFactoryBean { - - ValidatedLocalValidatorFactoryBean(ApplicationContext applicationContext) { - setApplicationContext(applicationContext); - setMessageInterpolator(new MessageInterpolatorFactory().getObject()); - afterPropertiesSet(); - } - - @Override - public boolean supports(Class type) { - return super.supports(type) - && AnnotatedElementUtils.hasAnnotation(type, Validated.class); - } - -} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java index ea1f06e9438..d5c50d82e41 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java @@ -55,7 +55,6 @@ public class ValidationExceptionFailureAnalyzerTests { static class TestConfiguration { TestConfiguration(TestProperties testProperties) { - } }