|
|
@ -17,7 +17,6 @@
|
|
|
|
package org.springframework.boot.context.properties;
|
|
|
|
package org.springframework.boot.context.properties;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
@ -34,7 +33,15 @@ import org.springframework.beans.factory.ListableBeanFactory;
|
|
|
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
|
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
|
|
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
|
|
|
import org.springframework.boot.bind.PropertiesConfigurationFactory;
|
|
|
|
import org.springframework.boot.context.properties.bind.BindHandler;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.Bindable;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.Binder;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.handler.IgnoreNestedPropertiesBindHandler;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.handler.NoUnboundElementsBindHandler;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.bind.validation.ValidationBindHandler;
|
|
|
|
|
|
|
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
|
|
|
import org.springframework.boot.validation.MessageInterpolatorFactory;
|
|
|
|
import org.springframework.boot.validation.MessageInterpolatorFactory;
|
|
|
|
import org.springframework.context.ApplicationContext;
|
|
|
|
import org.springframework.context.ApplicationContext;
|
|
|
|
import org.springframework.context.ApplicationContextAware;
|
|
|
|
import org.springframework.context.ApplicationContextAware;
|
|
|
@ -53,13 +60,10 @@ import org.springframework.core.convert.converter.GenericConverter;
|
|
|
|
import org.springframework.core.convert.support.DefaultConversionService;
|
|
|
|
import org.springframework.core.convert.support.DefaultConversionService;
|
|
|
|
import org.springframework.core.env.ConfigurableEnvironment;
|
|
|
|
import org.springframework.core.env.ConfigurableEnvironment;
|
|
|
|
import org.springframework.core.env.Environment;
|
|
|
|
import org.springframework.core.env.Environment;
|
|
|
|
import org.springframework.core.env.MutablePropertySources;
|
|
|
|
|
|
|
|
import org.springframework.core.env.PropertySource;
|
|
|
|
|
|
|
|
import org.springframework.core.env.PropertySources;
|
|
|
|
import org.springframework.core.env.PropertySources;
|
|
|
|
import org.springframework.core.env.StandardEnvironment;
|
|
|
|
import org.springframework.core.env.StandardEnvironment;
|
|
|
|
import org.springframework.util.Assert;
|
|
|
|
import org.springframework.util.Assert;
|
|
|
|
import org.springframework.util.ClassUtils;
|
|
|
|
import org.springframework.util.ClassUtils;
|
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
|
|
import org.springframework.validation.Errors;
|
|
|
|
import org.springframework.validation.Errors;
|
|
|
|
import org.springframework.validation.Validator;
|
|
|
|
import org.springframework.validation.Validator;
|
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
@ -73,6 +77,7 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
|
|
|
* @author Phillip Webb
|
|
|
|
* @author Phillip Webb
|
|
|
|
* @author Christian Dupuis
|
|
|
|
* @author Christian Dupuis
|
|
|
|
* @author Stephane Nicoll
|
|
|
|
* @author Stephane Nicoll
|
|
|
|
|
|
|
|
* @author Madhura Bhave
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
|
|
|
|
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
|
|
|
|
BeanFactoryAware, EnvironmentAware, ApplicationContextAware, InitializingBean,
|
|
|
|
BeanFactoryAware, EnvironmentAware, ApplicationContextAware, InitializingBean,
|
|
|
@ -113,6 +118,10 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
|
|
|
|
|
|
|
|
private int order = Ordered.HIGHEST_PRECEDENCE + 1;
|
|
|
|
private int order = Ordered.HIGHEST_PRECEDENCE + 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ConfigurationPropertySources configurationSources;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Binder binder;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* A list of custom converters (in addition to the defaults) to use when converting
|
|
|
|
* A list of custom converters (in addition to the defaults) to use when converting
|
|
|
|
* properties for binding.
|
|
|
|
* properties for binding.
|
|
|
@ -212,6 +221,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
|
|
|
|
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
|
|
|
|
ConversionService.class);
|
|
|
|
ConversionService.class);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.configurationSources = ConfigurationPropertySources
|
|
|
|
|
|
|
|
.get(this.propertySources);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
@ -240,18 +251,13 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
private PropertySources deducePropertySources() {
|
|
|
|
private PropertySources deducePropertySources() {
|
|
|
|
PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
|
|
|
|
PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
|
|
|
|
if (configurer != null) {
|
|
|
|
if (configurer != null) {
|
|
|
|
// Flatten the sources into a single list so they can be iterated
|
|
|
|
return configurer.getAppliedPropertySources();
|
|
|
|
return new FlatPropertySources(configurer.getAppliedPropertySources());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this.environment instanceof ConfigurableEnvironment) {
|
|
|
|
if (this.environment instanceof ConfigurableEnvironment) {
|
|
|
|
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
|
|
|
|
return ((ConfigurableEnvironment) this.environment).getPropertySources();
|
|
|
|
.getPropertySources();
|
|
|
|
|
|
|
|
return new FlatPropertySources(propertySources);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// empty, so not very useful, but fulfils the contract
|
|
|
|
throw new IllegalStateException("Unable to obtain PropertySources from "
|
|
|
|
logger.warn("Unable to obtain PropertySources from "
|
|
|
|
|
|
|
|
+ "PropertySourcesPlaceholderConfigurer or Environment");
|
|
|
|
+ "PropertySourcesPlaceholderConfigurer or Environment");
|
|
|
|
return new MutablePropertySources();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
|
|
|
|
private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
|
|
|
@ -287,15 +293,16 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
throws BeansException {
|
|
|
|
throws BeansException {
|
|
|
|
ConfigurationProperties annotation = AnnotationUtils
|
|
|
|
ConfigurationProperties annotation = AnnotationUtils
|
|
|
|
.findAnnotation(bean.getClass(), ConfigurationProperties.class);
|
|
|
|
.findAnnotation(bean.getClass(), ConfigurationProperties.class);
|
|
|
|
|
|
|
|
Object bound = bean;
|
|
|
|
if (annotation != null) {
|
|
|
|
if (annotation != null) {
|
|
|
|
postProcessBeforeInitialization(bean, beanName, annotation);
|
|
|
|
bound = postProcessBeforeInitialization(bean, beanName, annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
annotation = this.beans.findFactoryAnnotation(beanName,
|
|
|
|
annotation = this.beans.findFactoryAnnotation(beanName,
|
|
|
|
ConfigurationProperties.class);
|
|
|
|
ConfigurationProperties.class);
|
|
|
|
if (annotation != null) {
|
|
|
|
if (annotation != null) {
|
|
|
|
postProcessBeforeInitialization(bean, beanName, annotation);
|
|
|
|
bound = postProcessBeforeInitialization(bean, beanName, annotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bean;
|
|
|
|
return bound;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
@ -304,35 +311,53 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
return bean;
|
|
|
|
return bean;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void postProcessBeforeInitialization(Object bean, String beanName,
|
|
|
|
private Object postProcessBeforeInitialization(Object bean, String beanName,
|
|
|
|
ConfigurationProperties annotation) {
|
|
|
|
ConfigurationProperties annotation) {
|
|
|
|
Object target = bean;
|
|
|
|
Binder binder = getBinder();
|
|
|
|
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<>(
|
|
|
|
Validator validator = determineValidator(bean);
|
|
|
|
target);
|
|
|
|
BindHandler handler = getBindHandler(annotation, validator);
|
|
|
|
factory.setPropertySources(this.propertySources);
|
|
|
|
Bindable<?> bindable = Bindable.ofInstance(bean);
|
|
|
|
factory.setValidator(determineValidator(bean));
|
|
|
|
|
|
|
|
// If no explicit conversion service is provided we add one so that (at least)
|
|
|
|
|
|
|
|
// comma-separated arrays of convertibles can be bound automatically
|
|
|
|
|
|
|
|
factory.setConversionService(this.conversionService == null
|
|
|
|
|
|
|
|
? getDefaultConversionService() : this.conversionService);
|
|
|
|
|
|
|
|
if (annotation != null) {
|
|
|
|
|
|
|
|
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
|
|
|
|
|
|
|
|
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
|
|
|
|
|
|
|
|
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
|
|
|
|
|
|
|
|
if (StringUtils.hasLength(annotation.prefix())) {
|
|
|
|
|
|
|
|
factory.setTargetName(annotation.prefix());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
factory.bindPropertiesToTarget();
|
|
|
|
binder.bind(annotation.prefix(), bindable, handler);
|
|
|
|
|
|
|
|
return bean;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex) {
|
|
|
|
catch (Exception ex) {
|
|
|
|
String targetClass = ClassUtils.getShortName(target.getClass());
|
|
|
|
String targetClass = ClassUtils.getShortName(bean.getClass());
|
|
|
|
throw new BeanCreationException(beanName, "Could not bind properties to "
|
|
|
|
throw new BeanCreationException(beanName, "Could not bind properties to "
|
|
|
|
+ targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
|
|
|
|
+ targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Binder getBinder() {
|
|
|
|
|
|
|
|
Binder binder = this.binder;
|
|
|
|
|
|
|
|
if (binder == null) {
|
|
|
|
|
|
|
|
ConversionService conversionService = this.conversionService;
|
|
|
|
|
|
|
|
if (conversionService == null) {
|
|
|
|
|
|
|
|
conversionService = getDefaultConversionService();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
binder = new Binder(this.configurationSources,
|
|
|
|
|
|
|
|
new PropertySourcesPlaceholdersResolver(this.propertySources),
|
|
|
|
|
|
|
|
conversionService);
|
|
|
|
|
|
|
|
this.binder = binder;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return binder;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ConversionService getDefaultConversionService() {
|
|
|
|
|
|
|
|
if (this.defaultConversionService == null) {
|
|
|
|
|
|
|
|
DefaultConversionService conversionService = new DefaultConversionService();
|
|
|
|
|
|
|
|
this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
|
|
|
|
|
|
|
|
for (Converter<?, ?> converter : this.converters) {
|
|
|
|
|
|
|
|
conversionService.addConverter(converter);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (GenericConverter genericConverter : this.genericConverters) {
|
|
|
|
|
|
|
|
conversionService.addConverter(genericConverter);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
this.defaultConversionService = conversionService;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.defaultConversionService;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private String getAnnotationDetails(ConfigurationProperties annotation) {
|
|
|
|
private String getAnnotationDetails(ConfigurationProperties annotation) {
|
|
|
|
if (annotation == null) {
|
|
|
|
if (annotation == null) {
|
|
|
|
return "";
|
|
|
|
return "";
|
|
|
@ -379,19 +404,22 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private ConversionService getDefaultConversionService() {
|
|
|
|
private BindHandler getBindHandler(ConfigurationProperties annotation,
|
|
|
|
if (this.defaultConversionService == null) {
|
|
|
|
Validator validator) {
|
|
|
|
DefaultConversionService conversionService = new DefaultConversionService();
|
|
|
|
BindHandler handler = BindHandler.DEFAULT;
|
|
|
|
this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
|
|
|
|
if (annotation.ignoreInvalidFields()) {
|
|
|
|
for (Converter<?, ?> converter : this.converters) {
|
|
|
|
handler = new IgnoreErrorsBindHandler(handler);
|
|
|
|
conversionService.addConverter(converter);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (GenericConverter genericConverter : this.genericConverters) {
|
|
|
|
if (!annotation.ignoreUnknownFields()) {
|
|
|
|
conversionService.addConverter(genericConverter);
|
|
|
|
handler = new NoUnboundElementsBindHandler(handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.defaultConversionService = conversionService;
|
|
|
|
if (annotation.ignoreNestedProperties()) {
|
|
|
|
|
|
|
|
handler = new IgnoreNestedPropertiesBindHandler(handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.defaultConversionService;
|
|
|
|
if (validator != null) {
|
|
|
|
|
|
|
|
handler = new ValidationBindHandler(handler, validator);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -465,56 +493,4 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Convenience class to flatten out a tree of property sources without losing the
|
|
|
|
|
|
|
|
* reference to the backing data (which can therefore be updated in the background).
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private static class FlatPropertySources implements PropertySources {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private PropertySources propertySources;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FlatPropertySources(PropertySources propertySources) {
|
|
|
|
|
|
|
|
this.propertySources = propertySources;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public Iterator<PropertySource<?>> iterator() {
|
|
|
|
|
|
|
|
MutablePropertySources result = getFlattened();
|
|
|
|
|
|
|
|
return result.iterator();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public boolean contains(String name) {
|
|
|
|
|
|
|
|
return get(name) != null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public PropertySource<?> get(String name) {
|
|
|
|
|
|
|
|
return getFlattened().get(name);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private MutablePropertySources getFlattened() {
|
|
|
|
|
|
|
|
MutablePropertySources result = new MutablePropertySources();
|
|
|
|
|
|
|
|
for (PropertySource<?> propertySource : this.propertySources) {
|
|
|
|
|
|
|
|
flattenPropertySources(propertySource, result);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void flattenPropertySources(PropertySource<?> propertySource,
|
|
|
|
|
|
|
|
MutablePropertySources result) {
|
|
|
|
|
|
|
|
Object source = propertySource.getSource();
|
|
|
|
|
|
|
|
if (source instanceof ConfigurableEnvironment) {
|
|
|
|
|
|
|
|
ConfigurableEnvironment environment = (ConfigurableEnvironment) source;
|
|
|
|
|
|
|
|
for (PropertySource<?> childSource : environment.getPropertySources()) {
|
|
|
|
|
|
|
|
flattenPropertySources(childSource, result);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
result.addLast(propertySource);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|