From f3bcf94fb0d66dfb65f3a4b951f36dddd8225da4 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 10 Dec 2015 17:01:55 +0000 Subject: [PATCH] Refine validator cleanup logic Refine the validator memory optimization so that only directly created validators are nulled out. Also update the logic to ensure that `destroy` is also called. See gh-4734 --- ...urationPropertiesBindingPostProcessor.java | 82 ++++++++++--------- 1 file changed, 45 insertions(+), 37 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 72339f9da67..c68acd7618d 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 @@ -90,9 +90,9 @@ public class ConfigurationPropertiesBindingPostProcessor private PropertySources propertySources; - private volatile Validator validator; + private Validator validator; - private boolean ownedValidator = false; + private volatile Validator localValidator; private ConversionService conversionService; @@ -195,7 +195,9 @@ public class ConfigurationPropertiesBindingPostProcessor if (this.propertySources == null) { this.propertySources = deducePropertySources(); } - initializeValidator(); + if (this.validator == null) { + this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class); + } if (this.conversionService == null) { this.conversionService = getOptionalBean( ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME, @@ -205,36 +207,24 @@ public class ConfigurationPropertiesBindingPostProcessor @Override public void onApplicationEvent(ContextRefreshedEvent event) { - if (this.ownedValidator && this.validator != null && isJsr303Present()) { - this.validator = null; // allow it to be garbage collected - } + freeLocalValidator(); } - private void initializeValidator() { - if (this.validator == null) { - this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class); - if (this.validator == null && isJsr303Present()) { - this.validator = new Jsr303ValidatorFactory() - .run(this.applicationContext); - this.ownedValidator = true; - } - } + @Override + public void destroy() throws Exception { + freeLocalValidator(); } - private boolean isJsr303Present() { - for (String validatorClass : VALIDATOR_CLASSES) { - if (!ClassUtils.isPresent(validatorClass, - this.applicationContext.getClassLoader())) { - return false; + private void freeLocalValidator() { + try { + Validator validator = this.localValidator; + this.localValidator = null; + if (validator != null) { + ((DisposableBean) validator).destroy(); } } - return true; - } - - @Override - public void destroy() throws Exception { - if (this.ownedValidator && this.validator != null) { - ((DisposableBean) this.validator).destroy(); + catch (Exception ex) { + throw new IllegalStateException(ex); } } @@ -244,13 +234,11 @@ public class ConfigurationPropertiesBindingPostProcessor // Flatten the sources into a single list so they can be iterated return new FlatPropertySources(configurer.getAppliedPropertySources()); } - if (this.environment instanceof ConfigurableEnvironment) { MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment) .getPropertySources(); return new FlatPropertySources(propertySources); } - // empty, so not very useful, but fulfils the contract return new MutablePropertySources(); } @@ -353,16 +341,36 @@ public class ConfigurationPropertiesBindingPostProcessor } private Validator determineValidator(Object bean) { - initializeValidator(); - boolean globalValidatorSupportBean = (this.validator != null - && this.validator.supports(bean.getClass())); + Validator validator = getValidator(); + boolean supportsBean = (validator != null && validator.supports(bean.getClass())); if (ClassUtils.isAssignable(Validator.class, bean.getClass())) { - if (!globalValidatorSupportBean) { - return (Validator) bean; + if (supportsBean) { + return new ChainingValidator(validator, (Validator) bean); } - return new ChainingValidator(this.validator, (Validator) bean); + return (Validator) bean; } - return (globalValidatorSupportBean ? this.validator : null); + return (supportsBean ? validator : null); + } + + private Validator getValidator() { + if (this.validator != null) { + return this.validator; + } + if (this.localValidator == null && isJsr303Present()) { + this.localValidator = new LocalValidatorFactory() + .run(this.applicationContext); + } + return this.localValidator; + } + + private boolean isJsr303Present() { + for (String validatorClass : VALIDATOR_CLASSES) { + if (!ClassUtils.isPresent(validatorClass, + this.applicationContext.getClassLoader())) { + return false; + } + } + return true; } private PropertySources loadPropertySources(String[] locations, @@ -408,7 +416,7 @@ public class ConfigurationPropertiesBindingPostProcessor * Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent class * loader issues. */ - private static class Jsr303ValidatorFactory { + private static class LocalValidatorFactory { public Validator run(ApplicationContext applicationContext) { LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();