From e8632d9feb5c99de521bdb3dcf3bc8b886b1041f Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 9 Dec 2015 17:27:52 +0000 Subject: [PATCH] Null out validator in ContextRefreshedEvent This is an optimization that recognizes the need for Hibernate validator to cache reflection data, but also that we don't need it after the bulk of the work has been done on context refresh. Fixes gh-4734 --- ...urationPropertiesBindingPostProcessor.java | 35 +++++++++++++------ ...onPropertiesBindingPostProcessorTests.java | 12 +++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java index 3e4707f4ba7..b3efdaa69a0 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java @@ -36,9 +36,11 @@ import org.springframework.boot.bind.PropertiesConfigurationFactory; import org.springframework.boot.env.PropertySourcesLoader; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; @@ -71,9 +73,10 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; * @author Christian Dupuis * @author Stephane Nicoll */ -public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, - BeanFactoryAware, ResourceLoaderAware, EnvironmentAware, ApplicationContextAware, - InitializingBean, DisposableBean, PriorityOrdered { +public class ConfigurationPropertiesBindingPostProcessor + implements BeanPostProcessor, BeanFactoryAware, ResourceLoaderAware, + EnvironmentAware, ApplicationContextAware, InitializingBean, DisposableBean, + ApplicationListener, PriorityOrdered { /** * The bean name of the configuration properties validator. @@ -87,7 +90,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc private PropertySources propertySources; - private Validator validator; + private volatile Validator validator; private boolean ownedValidator = false; @@ -192,6 +195,22 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc if (this.propertySources == null) { this.propertySources = deducePropertySources(); } + initializeValidator(); + if (this.conversionService == null) { + this.conversionService = getOptionalBean( + ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, + ConversionService.class); + } + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + if (this.validator != null && isJsr303Present()) { + this.validator = null; // allow it to be garbage collected + } + } + + private void initializeValidator() { if (this.validator == null) { this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class); if (this.validator == null && isJsr303Present()) { @@ -200,11 +219,6 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc this.ownedValidator = true; } } - if (this.conversionService == null) { - this.conversionService = getOptionalBean( - ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, - ConversionService.class); - } } private boolean isJsr303Present() { @@ -219,7 +233,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc @Override public void destroy() throws Exception { - if (this.ownedValidator) { + if (this.ownedValidator && this.validator != null) { ((DisposableBean) this.validator).destroy(); } } @@ -339,6 +353,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc } private Validator determineValidator(Object bean) { + initializeValidator(); boolean globalValidatorSupportBean = (this.validator != null && this.validator.supports(bean.getClass())); if (ClassUtils.isAssignable(Validator.class, bean.getClass())) { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java index a67e9e13d1f..49d1cfa7dfc 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java @@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; @@ -51,6 +52,7 @@ import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -116,6 +118,16 @@ public class ConfigurationPropertiesBindingPostProcessorTests { assertBindingFailure(2); } + @Test + public void testValidationAndNullOutValidator() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(TestConfiguration.class); + this.context.refresh(); + ConfigurationPropertiesBindingPostProcessor bean = this.context + .getBean(ConfigurationPropertiesBindingPostProcessor.class); + assertNull(ReflectionTestUtils.getField(bean, "validator")); + } + @Test public void testSuccessfulValidationWithJSR303() { MockEnvironment env = new MockEnvironment();