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
This commit is contained in:
		
							parent
							
								
									bbbe4fdcd0
								
							
						
					
					
						commit
						f3bcf94fb0
					
				| 
						 | 
					@ -90,9 +90,9 @@ public class ConfigurationPropertiesBindingPostProcessor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private PropertySources propertySources;
 | 
						private PropertySources propertySources;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private volatile Validator validator;
 | 
						private Validator validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private boolean ownedValidator = false;
 | 
						private volatile Validator localValidator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private ConversionService conversionService;
 | 
						private ConversionService conversionService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -195,7 +195,9 @@ public class ConfigurationPropertiesBindingPostProcessor
 | 
				
			||||||
		if (this.propertySources == null) {
 | 
							if (this.propertySources == null) {
 | 
				
			||||||
			this.propertySources = deducePropertySources();
 | 
								this.propertySources = deducePropertySources();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		initializeValidator();
 | 
							if (this.validator == null) {
 | 
				
			||||||
 | 
								this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (this.conversionService == null) {
 | 
							if (this.conversionService == null) {
 | 
				
			||||||
			this.conversionService = getOptionalBean(
 | 
								this.conversionService = getOptionalBean(
 | 
				
			||||||
					ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
 | 
										ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
 | 
				
			||||||
| 
						 | 
					@ -205,36 +207,24 @@ public class ConfigurationPropertiesBindingPostProcessor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void onApplicationEvent(ContextRefreshedEvent event) {
 | 
						public void onApplicationEvent(ContextRefreshedEvent event) {
 | 
				
			||||||
		if (this.ownedValidator && this.validator != null && isJsr303Present()) {
 | 
							freeLocalValidator();
 | 
				
			||||||
			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()) {
 | 
					 | 
				
			||||||
				this.validator = new Jsr303ValidatorFactory()
 | 
					 | 
				
			||||||
						.run(this.applicationContext);
 | 
					 | 
				
			||||||
				this.ownedValidator = true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private boolean isJsr303Present() {
 | 
					 | 
				
			||||||
		for (String validatorClass : VALIDATOR_CLASSES) {
 | 
					 | 
				
			||||||
			if (!ClassUtils.isPresent(validatorClass,
 | 
					 | 
				
			||||||
					this.applicationContext.getClassLoader())) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return true;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void destroy() throws Exception {
 | 
						public void destroy() throws Exception {
 | 
				
			||||||
		if (this.ownedValidator && this.validator != null) {
 | 
							freeLocalValidator();
 | 
				
			||||||
			((DisposableBean) this.validator).destroy();
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void freeLocalValidator() {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								Validator validator = this.localValidator;
 | 
				
			||||||
 | 
								this.localValidator = null;
 | 
				
			||||||
 | 
								if (validator != null) {
 | 
				
			||||||
 | 
									((DisposableBean) 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
 | 
								// Flatten the sources into a single list so they can be iterated
 | 
				
			||||||
			return new FlatPropertySources(configurer.getAppliedPropertySources());
 | 
								return new FlatPropertySources(configurer.getAppliedPropertySources());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (this.environment instanceof ConfigurableEnvironment) {
 | 
							if (this.environment instanceof ConfigurableEnvironment) {
 | 
				
			||||||
			MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
 | 
								MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
 | 
				
			||||||
					.getPropertySources();
 | 
										.getPropertySources();
 | 
				
			||||||
			return new FlatPropertySources(propertySources);
 | 
								return new FlatPropertySources(propertySources);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// empty, so not very useful, but fulfils the contract
 | 
							// empty, so not very useful, but fulfils the contract
 | 
				
			||||||
		return new MutablePropertySources();
 | 
							return new MutablePropertySources();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -353,16 +341,36 @@ public class ConfigurationPropertiesBindingPostProcessor
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Validator determineValidator(Object bean) {
 | 
						private Validator determineValidator(Object bean) {
 | 
				
			||||||
		initializeValidator();
 | 
							Validator validator = getValidator();
 | 
				
			||||||
		boolean globalValidatorSupportBean = (this.validator != null
 | 
							boolean supportsBean = (validator != null && validator.supports(bean.getClass()));
 | 
				
			||||||
				&& this.validator.supports(bean.getClass()));
 | 
					 | 
				
			||||||
		if (ClassUtils.isAssignable(Validator.class, bean.getClass())) {
 | 
							if (ClassUtils.isAssignable(Validator.class, bean.getClass())) {
 | 
				
			||||||
			if (!globalValidatorSupportBean) {
 | 
								if (supportsBean) {
 | 
				
			||||||
				return (Validator) bean;
 | 
									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,
 | 
						private PropertySources loadPropertySources(String[] locations,
 | 
				
			||||||
| 
						 | 
					@ -408,7 +416,7 @@ public class ConfigurationPropertiesBindingPostProcessor
 | 
				
			||||||
	 * Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent class
 | 
						 * Factory to create JSR 303 LocalValidatorFactoryBean. Inner class to prevent class
 | 
				
			||||||
	 * loader issues.
 | 
						 * loader issues.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static class Jsr303ValidatorFactory {
 | 
						private static class LocalValidatorFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public Validator run(ApplicationContext applicationContext) {
 | 
							public Validator run(ApplicationContext applicationContext) {
 | 
				
			||||||
			LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
 | 
								LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue