Perform binding at creation time if possible
Previously, environment binding always happened in a post processor once the bean has been created. Constructor binding requires to perform the binding at creating time so this commit performs binding at creation time if possible. When this happens, a special `ConfigurationPropertiesBeanDefinition` is created with a supplier that invokes the binder. To avoid a case where a bean is processed twice, the post-processor now ignores any bean that has already been bound to the environment. Closes gh-8762 Co-authored-by: Madhura Bhave <mbhave@pivotal.io>
This commit is contained in:
parent
7ca589d43c
commit
430571b37b
|
@ -20,6 +20,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -46,6 +47,7 @@ public class JmxEndpointProperties {
|
|||
*/
|
||||
private final Properties staticNames = new Properties();
|
||||
|
||||
@Autowired
|
||||
public JmxEndpointProperties(Environment environment) {
|
||||
String defaultDomain = environment.getProperty("spring.jmx.default-domain");
|
||||
if (StringUtils.hasText(defaultDomain)) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
@ -25,6 +25,7 @@ import java.util.Set;
|
|||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
|
@ -58,6 +59,7 @@ public class SessionProperties {
|
|||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
@Autowired
|
||||
public SessionProperties(ObjectProvider<ServerProperties> serverProperties) {
|
||||
this.serverProperties = serverProperties.getIfUnique();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.lang.annotation.Annotation;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinition} that is used for registering {@link ConfigurationProperties}
|
||||
* beans that are bound at creation time.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
final class ConfigurationPropertiesBeanDefinition extends GenericBeanDefinition {
|
||||
|
||||
static ConfigurationPropertiesBeanDefinition from(
|
||||
ConfigurableListableBeanFactory beanFactory, String beanName, Class<?> type) {
|
||||
ConfigurationPropertiesBeanDefinition beanDefinition = new ConfigurationPropertiesBeanDefinition();
|
||||
beanDefinition.setBeanClass(type);
|
||||
beanDefinition.setInstanceSupplier(createBean(beanFactory, beanName, type));
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
private static <T> Supplier<T> createBean(ConfigurableListableBeanFactory beanFactory,
|
||||
String beanName, Class<T> type) {
|
||||
return () -> {
|
||||
ConfigurationProperties annotation = getAnnotation(type,
|
||||
ConfigurationProperties.class);
|
||||
Validated validated = getAnnotation(type, Validated.class);
|
||||
Annotation[] annotations = (validated != null)
|
||||
? new Annotation[] { annotation, validated }
|
||||
: new Annotation[] { annotation };
|
||||
Bindable<T> bindable = Bindable.of(type).withAnnotations(annotations);
|
||||
ConfigurationPropertiesBinder binder = beanFactory.getBean(
|
||||
ConfigurationPropertiesBinder.BEAN_NAME,
|
||||
ConfigurationPropertiesBinder.class);
|
||||
try {
|
||||
return binder.bind(bindable).orElseCreate(type);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new ConfigurationPropertiesBindException(beanName, type, annotation,
|
||||
ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static <A extends Annotation> A getAnnotation(Class<?> type,
|
||||
Class<A> annotationType) {
|
||||
return AnnotationUtils.findAnnotation(type, annotationType);
|
||||
}
|
||||
|
||||
}
|
|
@ -33,10 +33,10 @@ public class ConfigurationPropertiesBindException extends BeanCreationException
|
|||
|
||||
private final ConfigurationProperties annotation;
|
||||
|
||||
ConfigurationPropertiesBindException(String beanName, Object bean,
|
||||
ConfigurationPropertiesBindException(String beanName, Class<?> beanType,
|
||||
ConfigurationProperties annotation, Exception cause) {
|
||||
super(beanName, getMessage(bean, annotation), cause);
|
||||
this.beanType = bean.getClass();
|
||||
super(beanName, getMessage(beanType, annotation), cause);
|
||||
this.beanType = beanType;
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
|
@ -56,10 +56,11 @@ public class ConfigurationPropertiesBindException extends BeanCreationException
|
|||
return this.annotation;
|
||||
}
|
||||
|
||||
private static String getMessage(Object bean, ConfigurationProperties annotation) {
|
||||
private static String getMessage(Class<?> beanType,
|
||||
ConfigurationProperties annotation) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append("Could not bind properties to '");
|
||||
message.append(ClassUtils.getShortName(bean.getClass())).append("' : ");
|
||||
message.append(ClassUtils.getShortName(beanType)).append("' : ");
|
||||
message.append("prefix=").append(annotation.prefix());
|
||||
message.append(", ignoreInvalidFields=").append(annotation.ignoreInvalidFields());
|
||||
message.append(", ignoreUnknownFields=").append(annotation.ignoreUnknownFields());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
@ -21,8 +21,10 @@ import java.util.List;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.boot.context.properties.bind.BindHandler;
|
||||
import org.springframework.boot.context.properties.bind.BindResult;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
|
||||
|
@ -34,6 +36,7 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS
|
|||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
import org.springframework.boot.context.properties.source.UnboundElementsSourceFilter;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
|
@ -48,39 +51,51 @@ import org.springframework.validation.annotation.Validated;
|
|||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConfigurationPropertiesBinder {
|
||||
class ConfigurationPropertiesBinder implements ApplicationContextAware {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
/**
|
||||
* The bean name that this binder is registered with.
|
||||
*/
|
||||
static final String BEAN_NAME = "org.springframework.boot.context.internalConfigurationPropertiesBinder";
|
||||
|
||||
private final PropertySources propertySources;
|
||||
private final String validatorBeanName;
|
||||
|
||||
private final Validator configurationPropertiesValidator;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private final boolean jsr303Present;
|
||||
private PropertySources propertySources;
|
||||
|
||||
private Validator configurationPropertiesValidator;
|
||||
|
||||
private boolean jsr303Present;
|
||||
|
||||
private volatile Validator jsr303Validator;
|
||||
|
||||
private volatile Binder binder;
|
||||
|
||||
ConfigurationPropertiesBinder(ApplicationContext applicationContext,
|
||||
String validatorBeanName) {
|
||||
ConfigurationPropertiesBinder(String validatorBeanName) {
|
||||
this.validatorBeanName = validatorBeanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
this.propertySources = new PropertySourcesDeducer(applicationContext)
|
||||
.getPropertySources();
|
||||
this.configurationPropertiesValidator = getConfigurationPropertiesValidator(
|
||||
applicationContext, validatorBeanName);
|
||||
applicationContext, this.validatorBeanName);
|
||||
this.jsr303Present = ConfigurationPropertiesJsr303Validator
|
||||
.isJsr303Present(applicationContext);
|
||||
}
|
||||
|
||||
public void bind(Bindable<?> target) {
|
||||
public <T> BindResult<T> bind(Bindable<T> target) {
|
||||
ConfigurationProperties annotation = target
|
||||
.getAnnotation(ConfigurationProperties.class);
|
||||
Assert.state(annotation != null,
|
||||
() -> "Missing @ConfigurationProperties on " + target);
|
||||
List<Validator> validators = getValidators(target);
|
||||
BindHandler bindHandler = getBindHandler(annotation, validators);
|
||||
getBinder().bind(annotation.prefix(), target, bindHandler);
|
||||
return getBinder().bind(annotation.prefix(), target, bindHandler);
|
||||
}
|
||||
|
||||
private Validator getConfigurationPropertiesValidator(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
@ -21,7 +21,9 @@ import java.lang.reflect.Method;
|
|||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
@ -53,8 +55,11 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||
|
||||
/**
|
||||
* The bean name of the configuration properties validator.
|
||||
* @deprecated see
|
||||
* {@link ConfigurationPropertiesBindingPostProcessorRegistrar#VALIDATOR_BEAN_NAME}
|
||||
*/
|
||||
public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
|
||||
@Deprecated
|
||||
public static final String VALIDATOR_BEAN_NAME = ConfigurationPropertiesBindingPostProcessorRegistrar.VALIDATOR_BEAN_NAME;
|
||||
|
||||
private ConfigurationBeanFactoryMetadata beanFactoryMetadata;
|
||||
|
||||
|
@ -75,8 +80,9 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||
this.beanFactoryMetadata = this.applicationContext.getBean(
|
||||
ConfigurationBeanFactoryMetadata.BEAN_NAME,
|
||||
ConfigurationBeanFactoryMetadata.class);
|
||||
this.configurationPropertiesBinder = new ConfigurationPropertiesBinder(
|
||||
this.applicationContext, VALIDATOR_BEAN_NAME);
|
||||
this.configurationPropertiesBinder = this.applicationContext.getBean(
|
||||
ConfigurationPropertiesBinder.BEAN_NAME,
|
||||
ConfigurationPropertiesBinder.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -89,12 +95,18 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||
throws BeansException {
|
||||
ConfigurationProperties annotation = getAnnotation(bean, beanName,
|
||||
ConfigurationProperties.class);
|
||||
if (annotation != null) {
|
||||
if (annotation != null && !hasBeenBound(beanName)) {
|
||||
bind(bean, beanName, annotation);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private boolean hasBeenBound(String beanName) {
|
||||
BeanDefinition beanDefinition = ((BeanDefinitionRegistry) this.applicationContext
|
||||
.getAutowireCapableBeanFactory()).getBeanDefinition(beanName);
|
||||
return beanDefinition instanceof ConfigurationPropertiesBeanDefinition;
|
||||
}
|
||||
|
||||
private void bind(Object bean, String beanName, ConfigurationProperties annotation) {
|
||||
ResolvableType type = getBeanType(bean, beanName);
|
||||
Validated validated = getAnnotation(bean, beanName, Validated.class);
|
||||
|
@ -107,8 +119,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
|
|||
this.configurationPropertiesBinder.bind(target);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new ConfigurationPropertiesBindException(beanName, bean, annotation,
|
||||
ex);
|
||||
throw new ConfigurationPropertiesBindException(beanName, bean.getClass(),
|
||||
annotation, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
@ -32,9 +32,17 @@ import org.springframework.core.type.AnnotationMetadata;
|
|||
public class ConfigurationPropertiesBindingPostProcessorRegistrar
|
||||
implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
/**
|
||||
* The bean name of the configuration properties validator.
|
||||
*/
|
||||
public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
if (!registry.containsBeanDefinition(ConfigurationPropertiesBinder.BEAN_NAME)) {
|
||||
registerConfigurationPropertiesBinder(registry);
|
||||
}
|
||||
if (!registry.containsBeanDefinition(
|
||||
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME)) {
|
||||
registerConfigurationPropertiesBindingPostProcessor(registry);
|
||||
|
@ -42,6 +50,16 @@ public class ConfigurationPropertiesBindingPostProcessorRegistrar
|
|||
}
|
||||
}
|
||||
|
||||
private void registerConfigurationPropertiesBinder(BeanDefinitionRegistry registry) {
|
||||
GenericBeanDefinition definition = new GenericBeanDefinition();
|
||||
definition.setBeanClass(ConfigurationPropertiesBinder.class);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
definition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
VALIDATOR_BEAN_NAME);
|
||||
registry.registerBeanDefinition(ConfigurationPropertiesBinder.BEAN_NAME,
|
||||
definition);
|
||||
}
|
||||
|
||||
private void registerConfigurationPropertiesBindingPostProcessor(
|
||||
BeanDefinitionRegistry registry) {
|
||||
GenericBeanDefinition definition = new GenericBeanDefinition();
|
||||
|
@ -49,7 +67,6 @@ public class ConfigurationPropertiesBindingPostProcessorRegistrar
|
|||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(
|
||||
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME, definition);
|
||||
|
||||
}
|
||||
|
||||
private void registerConfigurationBeanFactoryMetadata(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
@ -16,12 +16,15 @@
|
|||
|
||||
package org.springframework.boot.context.properties;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
|
@ -89,7 +92,7 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
|
|||
ConfigurableListableBeanFactory beanFactory, Class<?> type) {
|
||||
String name = getName(type);
|
||||
if (!containsBeanDefinition(beanFactory, name)) {
|
||||
registerBeanDefinition(registry, name, type);
|
||||
registerBeanDefinition(registry, beanFactory, name, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,12 +117,11 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void registerBeanDefinition(BeanDefinitionRegistry registry, String name,
|
||||
Class<?> type) {
|
||||
private void registerBeanDefinition(BeanDefinitionRegistry registry,
|
||||
ConfigurableListableBeanFactory beanFactory, String name, Class<?> type) {
|
||||
assertHasAnnotation(type);
|
||||
GenericBeanDefinition definition = new GenericBeanDefinition();
|
||||
definition.setBeanClass(type);
|
||||
registry.registerBeanDefinition(name, definition);
|
||||
registry.registerBeanDefinition(name,
|
||||
createBeanDefinition(beanFactory, name, type));
|
||||
}
|
||||
|
||||
private void assertHasAnnotation(Class<?> type) {
|
||||
|
@ -129,6 +131,29 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
|
|||
+ " annotation found on '" + type.getName() + "'.");
|
||||
}
|
||||
|
||||
private BeanDefinition createBeanDefinition(
|
||||
ConfigurableListableBeanFactory beanFactory, String name, Class<?> type) {
|
||||
if (canBindAtCreationTime(type)) {
|
||||
return ConfigurationPropertiesBeanDefinition.from(beanFactory, name,
|
||||
type);
|
||||
}
|
||||
else {
|
||||
GenericBeanDefinition definition = new GenericBeanDefinition();
|
||||
definition.setBeanClass(type);
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canBindAtCreationTime(Class<?> type) {
|
||||
Constructor<?>[] constructors = type.getDeclaredConstructors();
|
||||
boolean autowiredPresent = Arrays.stream(constructors).anyMatch(
|
||||
(c) -> AnnotationUtils.findAnnotation(c, Autowired.class) != null);
|
||||
if (autowiredPresent) {
|
||||
return false;
|
||||
}
|
||||
return (constructors.length == 1 && constructors[0].getParameterCount() > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -254,7 +254,8 @@ public class Binder {
|
|||
private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target,
|
||||
BindHandler handler, Context context, boolean allowRecursiveBinding) {
|
||||
ConfigurationProperty property = findProperty(name, context);
|
||||
if (property == null && containsNoDescendantOf(context.getSources(), name)) {
|
||||
if (property == null && containsNoDescendantOf(context.getSources(), name)
|
||||
&& context.depth != 0) {
|
||||
return null;
|
||||
}
|
||||
AggregateBinder<?> aggregateBinder = getAggregateBinder(target, context);
|
||||
|
@ -330,8 +331,7 @@ public class Binder {
|
|||
|
||||
private Object bindBean(ConfigurationPropertyName name, Bindable<?> target,
|
||||
BindHandler handler, Context context, boolean allowRecursiveBinding) {
|
||||
if (containsNoDescendantOf(context.getSources(), name)
|
||||
|| isUnbindableBean(name, target, context)) {
|
||||
if (isUnbindableBean(name, target, context)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> type = target.getType().resolve(Object.class);
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.springframework.beans.BeansException;
|
|||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
|
@ -805,6 +806,46 @@ public class ConfigurationPropertiesTests {
|
|||
assertThat(x.get(1).getB()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadWhenConfigurationPropertiesInjectsAnotherBeanShouldNotFail() {
|
||||
load(OtherInjectPropertiesConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadWhenBindingToConstructorParametersShouldBind() {
|
||||
MutablePropertySources sources = this.context.getEnvironment()
|
||||
.getPropertySources();
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("test.foo", "baz");
|
||||
source.put("test.bar", "5");
|
||||
sources.addLast(new MapPropertySource("test", source));
|
||||
load(ConstructorParameterConfiguration.class);
|
||||
ConstructorParameterProperties bean = this.context
|
||||
.getBean(ConstructorParameterProperties.class);
|
||||
assertThat(bean.getFoo()).isEqualTo("baz");
|
||||
assertThat(bean.getBar()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadWhenBindingToConstructorParametersWithDefaultValuesShouldBind() {
|
||||
load(ConstructorParameterConfiguration.class);
|
||||
ConstructorParameterProperties bean = this.context
|
||||
.getBean(ConstructorParameterProperties.class);
|
||||
assertThat(bean.getFoo()).isEqualTo("hello");
|
||||
assertThat(bean.getBar()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadWhenBindingToConstructorParametersShouldValidate() {
|
||||
assertThatExceptionOfType(Exception.class)
|
||||
.isThrownBy(() -> load(ConstructorParameterValidationConfiguration.class))
|
||||
.satisfies((ex) -> {
|
||||
assertThat(ex).hasCauseInstanceOf(BindException.class);
|
||||
assertThat(ex.getCause())
|
||||
.hasCauseExactlyInstanceOf(BindValidationException.class);
|
||||
});
|
||||
}
|
||||
|
||||
private AnnotationConfigApplicationContext load(Class<?> configuration,
|
||||
String... inlinedProperties) {
|
||||
return load(new Class<?>[] { configuration }, inlinedProperties);
|
||||
|
@ -1768,6 +1809,76 @@ public class ConfigurationPropertiesTests {
|
|||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "test")
|
||||
static class OtherInjectedProperties {
|
||||
|
||||
private final DataSizeProperties dataSizeProperties;
|
||||
|
||||
@Autowired
|
||||
OtherInjectedProperties(ObjectProvider<DataSizeProperties> dataSizeProperties) {
|
||||
this.dataSizeProperties = dataSizeProperties.getIfUnique();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(OtherInjectedProperties.class)
|
||||
static class OtherInjectPropertiesConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "test")
|
||||
@Validated
|
||||
static class ConstructorParameterProperties {
|
||||
|
||||
@NotEmpty
|
||||
private final String foo;
|
||||
|
||||
private final int bar;
|
||||
|
||||
ConstructorParameterProperties(
|
||||
@ConfigurationPropertyDefaultValue("hello") String foo, int bar) {
|
||||
this.foo = foo;
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
public String getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
public int getBar() {
|
||||
return this.bar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "test")
|
||||
@Validated
|
||||
static class ConstructorParameterValidatedProperties {
|
||||
|
||||
@NotEmpty
|
||||
private final String foo;
|
||||
|
||||
ConstructorParameterValidatedProperties(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
public String getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties(ConstructorParameterProperties.class)
|
||||
static class ConstructorParameterConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties(ConstructorParameterValidatedProperties.class)
|
||||
static class ConstructorParameterValidationConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class CustomPropertiesValidator implements Validator {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.io.IOException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link EnableConfigurationPropertiesImportSelector}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class EnableConfigurationPropertiesImportSelectorTests {
|
||||
|
||||
private final EnableConfigurationPropertiesImportSelector importSelector = new EnableConfigurationPropertiesImportSelector();
|
||||
|
||||
private final EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar registrar = new EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar();
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
@Test
|
||||
public void selectImports() {
|
||||
String[] imports = this.importSelector
|
||||
.selectImports(mock(AnnotationMetadata.class));
|
||||
assertThat(imports).containsExactly(
|
||||
EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar.class
|
||||
.getName(),
|
||||
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeWithDefaultConstructorShouldRegisterGenericBeanDefinition()
|
||||
throws Exception {
|
||||
this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(TestConfiguration.class), this.beanFactory);
|
||||
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(
|
||||
"foo-org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelectorTests$FooProperties");
|
||||
assertThat(beanDefinition).isExactlyInstanceOf(GenericBeanDefinition.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeWithAutowiredOnConstructorShouldRegisterGenericBeanDefinition()
|
||||
throws Exception {
|
||||
this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(TestConfiguration.class), this.beanFactory);
|
||||
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(
|
||||
"bar-org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelectorTests$BarProperties");
|
||||
assertThat(beanDefinition).isExactlyInstanceOf(GenericBeanDefinition.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeWithOneConstructorWithParametersShouldRegisterConfigurationPropertiesBeanDefinition()
|
||||
throws Exception {
|
||||
this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(TestConfiguration.class), this.beanFactory);
|
||||
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(
|
||||
"baz-org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelectorTests$BazProperties");
|
||||
assertThat(beanDefinition)
|
||||
.isExactlyInstanceOf(ConfigurationPropertiesBeanDefinition.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeWithMultipleConstructorsShouldRegisterGenericBeanDefinition()
|
||||
throws Exception {
|
||||
this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(TestConfiguration.class), this.beanFactory);
|
||||
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(
|
||||
"bing-org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelectorTests$BingProperties");
|
||||
assertThat(beanDefinition).isExactlyInstanceOf(GenericBeanDefinition.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeWithNoAnnotationShouldFail() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(InvalidConfiguration.class),
|
||||
this.beanFactory))
|
||||
.withMessageContaining("No ConfigurationProperties annotation found")
|
||||
.withMessageContaining(
|
||||
EnableConfigurationPropertiesImportSelectorTests.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationWithDuplicatedTypeShouldRegisterSingleBeanDefinition()
|
||||
throws IOException {
|
||||
DefaultListableBeanFactory factory = spy(this.beanFactory);
|
||||
this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(DuplicateConfiguration.class), factory);
|
||||
verify(factory, times(1)).registerBeanDefinition(anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationWithNoTypeShouldNotRegisterAnything() throws IOException {
|
||||
DefaultListableBeanFactory factory = spy(this.beanFactory);
|
||||
this.registrar.registerBeanDefinitions(
|
||||
getAnnotationMetadata(EmptyConfiguration.class), factory);
|
||||
verifyZeroInteractions(factory);
|
||||
}
|
||||
|
||||
private AnnotationMetadata getAnnotationMetadata(Class<?> source) throws IOException {
|
||||
return new SimpleMetadataReaderFactory().getMetadataReader(source.getName())
|
||||
.getAnnotationMetadata();
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties({ FooProperties.class, BarProperties.class,
|
||||
BazProperties.class, BingProperties.class })
|
||||
static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties(EnableConfigurationPropertiesImportSelectorTests.class)
|
||||
static class InvalidConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties({ FooProperties.class, FooProperties.class })
|
||||
static class DuplicateConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "foo")
|
||||
static class FooProperties {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
public static class BarProperties {
|
||||
|
||||
@Autowired
|
||||
public BarProperties(String foo) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "baz")
|
||||
public static class BazProperties {
|
||||
|
||||
public BazProperties(String foo) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
public static class BingProperties {
|
||||
|
||||
public BingProperties() {
|
||||
|
||||
}
|
||||
|
||||
public BingProperties(String foo) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
* Copyright 2012-2019 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.
|
||||
|
|
Loading…
Reference in New Issue