mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-15 01:07:30 +08:00
Move @ConstructorBinding annotation and support add default support
Relocate `@ConstructorBinding` from the `boot.context.properties` package to `boot.context.properties.bind` and update the `DefaultBindConstructorProvider` to support it. Closes gh-32660
This commit is contained in:
parent
db248b80bb
commit
e3df6c5b6f
@ -62,7 +62,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.context.properties.BoundConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBean;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBindConstructorProvider;
|
||||
import org.springframework.boot.context.properties.bind.BindConstructorProvider;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Name;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationProperty;
|
||||
@ -476,8 +476,7 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
||||
List<BeanPropertyWriter> result = new ArrayList<>();
|
||||
Class<?> beanClass = beanDesc.getType().getRawClass();
|
||||
Bindable<?> bindable = Bindable.of(ClassUtils.getUserClass(beanClass));
|
||||
Constructor<?> bindConstructor = ConfigurationPropertiesBindConstructorProvider.INSTANCE
|
||||
.getBindConstructor(bindable, false);
|
||||
Constructor<?> bindConstructor = BindConstructorProvider.DEFAULT.getBindConstructor(bindable, false);
|
||||
for (BeanPropertyWriter writer : beanProperties) {
|
||||
if (isCandidate(beanDesc, writer, bindConstructor)) {
|
||||
result.add(writer);
|
||||
|
@ -34,8 +34,8 @@ import org.springframework.boot.actuate.context.properties.ConfigurationProperti
|
||||
import org.springframework.boot.actuate.endpoint.SanitizingFunction;
|
||||
import org.springframework.boot.actuate.endpoint.Show;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.ConstructorBinding;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.ConstructorBinding;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.context.properties.bind.Name;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.boot.context.properties;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -30,6 +31,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.context.properties.bind.BindConstructorProvider;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@ -70,8 +72,8 @@ public final class ConfigurationPropertiesBean {
|
||||
private final BindMethod bindMethod;
|
||||
|
||||
private ConfigurationPropertiesBean(String name, Object instance, ConfigurationProperties annotation,
|
||||
Bindable<?> bindTarget) {
|
||||
this(name, instance, annotation, bindTarget, BindMethod.forType(bindTarget.getType().resolve()));
|
||||
Bindable<?> bindable) {
|
||||
this(name, instance, annotation, bindable, BindMethod.get(bindable));
|
||||
}
|
||||
|
||||
private ConfigurationPropertiesBean(String name, Object instance, ConfigurationProperties annotation,
|
||||
@ -274,14 +276,14 @@ public final class ConfigurationPropertiesBean {
|
||||
: new Annotation[] { annotation };
|
||||
ResolvableType bindType = (factory != null) ? ResolvableType.forMethodReturnType(factory)
|
||||
: ResolvableType.forClass(type);
|
||||
Bindable<Object> bindTarget = Bindable.of(bindType).withAnnotations(annotations);
|
||||
Bindable<Object> bindable = Bindable.of(bindType).withAnnotations(annotations);
|
||||
if (instance != null) {
|
||||
bindTarget = bindTarget.withExistingValue(instance);
|
||||
bindable = bindable.withExistingValue(instance);
|
||||
}
|
||||
if (factory != null) {
|
||||
return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget, BindMethod.JAVA_BEAN);
|
||||
return new ConfigurationPropertiesBean(name, instance, annotation, bindable, BindMethod.JAVA_BEAN);
|
||||
}
|
||||
return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);
|
||||
return new ConfigurationPropertiesBean(name, instance, annotation, bindable);
|
||||
}
|
||||
|
||||
private static <A extends Annotation> A findAnnotation(Object instance, Class<?> type, Method factory,
|
||||
@ -321,9 +323,16 @@ public final class ConfigurationPropertiesBean {
|
||||
*/
|
||||
VALUE_OBJECT;
|
||||
|
||||
static BindMethod forType(Class<?> type) {
|
||||
return (ConfigurationPropertiesBindConstructorProvider.INSTANCE.getBindConstructor(type, false) != null)
|
||||
? VALUE_OBJECT : JAVA_BEAN;
|
||||
static BindMethod get(Class<?> type) {
|
||||
return get(BindConstructorProvider.DEFAULT.getBindConstructor(type, false));
|
||||
}
|
||||
|
||||
static BindMethod get(Bindable<?> bindable) {
|
||||
return get(BindConstructorProvider.DEFAULT.getBindConstructor(bindable, false));
|
||||
}
|
||||
|
||||
private static BindMethod get(Constructor<?> bindConstructor) {
|
||||
return (bindConstructor != null) ? VALUE_OBJECT : JAVA_BEAN;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ final class ConfigurationPropertiesBeanRegistrar {
|
||||
}
|
||||
|
||||
private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
|
||||
BindMethod bindMethod = BindMethod.forType(type);
|
||||
BindMethod bindMethod = BindMethod.get(type);
|
||||
RootBeanDefinition definition = new RootBeanDefinition(type);
|
||||
definition.setAttribute(BindMethod.class.getName(), bindMethod);
|
||||
if (bindMethod == BindMethod.VALUE_OBJECT) {
|
||||
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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
|
||||
*
|
||||
* https://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.reflect.Constructor;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.bind.BindConstructorProvider;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link BindConstructorProvider} used when binding
|
||||
* {@link ConfigurationProperties @ConfigurationProperties}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class ConfigurationPropertiesBindConstructorProvider implements BindConstructorProvider {
|
||||
|
||||
/**
|
||||
* A shared singleton {@link ConfigurationPropertiesBindConstructorProvider} instance.
|
||||
*/
|
||||
public static final ConfigurationPropertiesBindConstructorProvider INSTANCE = new ConfigurationPropertiesBindConstructorProvider();
|
||||
|
||||
@Override
|
||||
public Constructor<?> getBindConstructor(Bindable<?> bindable, boolean isNestedConstructorBinding) {
|
||||
return getBindConstructor(bindable.getType().resolve(), isNestedConstructorBinding);
|
||||
}
|
||||
|
||||
Constructor<?> getBindConstructor(Class<?> type, boolean isNestedConstructorBinding) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
Constructors constructors = Constructors.getConstructors(type);
|
||||
if (constructors.getBind() != null || isNestedConstructorBinding) {
|
||||
Assert.state(!constructors.hasAutowired(),
|
||||
() -> type.getName() + " declares @ConstructorBinding and @Autowired constructor");
|
||||
}
|
||||
return constructors.getBind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data holder for autowired and bind constructors.
|
||||
*/
|
||||
static final class Constructors {
|
||||
|
||||
private final boolean hasAutowired;
|
||||
|
||||
private final Constructor<?> bind;
|
||||
|
||||
private Constructors(boolean hasAutowired, Constructor<?> bind) {
|
||||
this.hasAutowired = hasAutowired;
|
||||
this.bind = bind;
|
||||
}
|
||||
|
||||
boolean hasAutowired() {
|
||||
return this.hasAutowired;
|
||||
}
|
||||
|
||||
Constructor<?> getBind() {
|
||||
return this.bind;
|
||||
}
|
||||
|
||||
static Constructors getConstructors(Class<?> type) {
|
||||
Constructor<?>[] candidates = getCandidateConstructors(type);
|
||||
Constructor<?> deducedBind = deduceBindConstructor(candidates);
|
||||
if (deducedBind != null) {
|
||||
return new Constructors(false, deducedBind);
|
||||
}
|
||||
boolean hasAutowiredConstructor = false;
|
||||
Constructor<?> bind = null;
|
||||
for (Constructor<?> candidate : candidates) {
|
||||
if (isAutowired(candidate)) {
|
||||
hasAutowiredConstructor = true;
|
||||
continue;
|
||||
}
|
||||
bind = findAnnotatedConstructor(type, bind, candidate);
|
||||
}
|
||||
if (bind == null && !hasAutowiredConstructor && isKotlinType(type)) {
|
||||
bind = deduceKotlinBindConstructor(type);
|
||||
}
|
||||
return new Constructors(hasAutowiredConstructor, bind);
|
||||
}
|
||||
|
||||
private static Constructor<?>[] getCandidateConstructors(Class<?> type) {
|
||||
if (isInnerClass(type)) {
|
||||
return new Constructor<?>[0];
|
||||
}
|
||||
return Arrays.stream(type.getDeclaredConstructors())
|
||||
.filter((constructor) -> isNonSynthetic(constructor, type)).toArray(Constructor[]::new);
|
||||
}
|
||||
|
||||
private static boolean isInnerClass(Class<?> type) {
|
||||
try {
|
||||
return type.getDeclaredField("this$0").isSynthetic();
|
||||
}
|
||||
catch (NoSuchFieldException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNonSynthetic(Constructor<?> constructor, Class<?> type) {
|
||||
return !constructor.isSynthetic();
|
||||
}
|
||||
|
||||
private static Constructor<?> deduceBindConstructor(Constructor<?>[] constructors) {
|
||||
if (constructors.length == 1 && constructors[0].getParameterCount() > 0 && !isAutowired(constructors[0])) {
|
||||
return constructors[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isAutowired(Constructor<?> candidate) {
|
||||
return MergedAnnotations.from(candidate).isPresent(Autowired.class);
|
||||
}
|
||||
|
||||
private static Constructor<?> findAnnotatedConstructor(Class<?> type, Constructor<?> constructor,
|
||||
Constructor<?> candidate) {
|
||||
if (MergedAnnotations.from(candidate).isPresent(ConstructorBinding.class)) {
|
||||
Assert.state(candidate.getParameterCount() > 0,
|
||||
() -> type.getName() + " declares @ConstructorBinding on a no-args constructor");
|
||||
Assert.state(constructor == null,
|
||||
() -> type.getName() + " has more than one @ConstructorBinding constructor");
|
||||
constructor = candidate;
|
||||
}
|
||||
return constructor;
|
||||
}
|
||||
|
||||
private static boolean isKotlinType(Class<?> type) {
|
||||
return KotlinDetector.isKotlinPresent() && KotlinDetector.isKotlinType(type);
|
||||
}
|
||||
|
||||
private static Constructor<?> deduceKotlinBindConstructor(Class<?> type) {
|
||||
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(type);
|
||||
if (primaryConstructor != null && primaryConstructor.getParameterCount() > 0) {
|
||||
return primaryConstructor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -164,8 +164,7 @@ class ConfigurationPropertiesBinder {
|
||||
private Binder getBinder() {
|
||||
if (this.binder == null) {
|
||||
this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(),
|
||||
getConversionServices(), getPropertyEditorInitializer(), null,
|
||||
ConfigurationPropertiesBindConstructorProvider.INSTANCE);
|
||||
getConversionServices(), getPropertyEditorInitializer(), null, null);
|
||||
}
|
||||
return this.binder;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.beans.BeanInfoFactory;
|
||||
import org.springframework.beans.ExtendedBeanInfoFactory;
|
||||
import org.springframework.boot.context.properties.bind.BindConstructorProvider;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
@ -88,7 +89,7 @@ public final class ConfigurationPropertiesReflectionHintsProcessor {
|
||||
|
||||
private static Constructor<?> getBindConstructor(Class<?> type, boolean nestedType) {
|
||||
Bindable<?> bindable = Bindable.of(type);
|
||||
return ConfigurationPropertiesBindConstructorProvider.INSTANCE.getBindConstructor(bindable, nestedType);
|
||||
return BindConstructorProvider.DEFAULT.getBindConstructor(bindable, nestedType);
|
||||
}
|
||||
|
||||
private void process(ReflectionHints reflectionHints) {
|
||||
|
@ -39,10 +39,14 @@ import java.lang.annotation.Target;
|
||||
* @author Phillip Webb
|
||||
* @since 2.2.0
|
||||
* @see ConfigurationProperties
|
||||
* @deprecated since 3.0.0 for removal in 3.2.0 in favor of
|
||||
* {@link org.springframework.boot.context.properties.bind.ConstructorBinding}
|
||||
*/
|
||||
@Target(ElementType.CONSTRUCTOR)
|
||||
@Target({ ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Deprecated(since = "3.0.0", forRemoval = true)
|
||||
@org.springframework.boot.context.properties.bind.ConstructorBinding
|
||||
public @interface ConstructorBinding {
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@ -22,6 +22,7 @@ import org.springframework.beans.factory.InjectionPoint;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBean.BindMethod;
|
||||
import org.springframework.boot.context.properties.bind.ConstructorBinding;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
import org.springframework.boot.diagnostics.analyzer.AbstractInjectionFailureAnalyzer;
|
||||
import org.springframework.core.Ordered;
|
||||
@ -66,7 +67,7 @@ class NotConstructorBoundInjectionFailureAnalyzer
|
||||
MergedAnnotation<ConfigurationProperties> configurationProperties = MergedAnnotations.from(declaringClass)
|
||||
.get(ConfigurationProperties.class);
|
||||
return configurationProperties.isPresent()
|
||||
&& BindMethod.forType(constructor.getDeclaringClass()) == BindMethod.VALUE_OBJECT;
|
||||
&& BindMethod.get(constructor.getDeclaringClass()) == BindMethod.VALUE_OBJECT;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@ -33,6 +33,19 @@ public interface BindConstructorProvider {
|
||||
*/
|
||||
BindConstructorProvider DEFAULT = new DefaultBindConstructorProvider();
|
||||
|
||||
/**
|
||||
* Return the bind constructor to use for the given type, or {@code null} if
|
||||
* constructor binding is not supported.
|
||||
* @param type the type to check
|
||||
* @param isNestedConstructorBinding if this binding is nested within a constructor
|
||||
* binding
|
||||
* @return the bind constructor or {@code null}
|
||||
* @since 3.0.0
|
||||
*/
|
||||
default Constructor<?> getBindConstructor(Class<?> type, boolean isNestedConstructorBinding) {
|
||||
return getBindConstructor(Bindable.of(type), isNestedConstructorBinding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind constructor to use for the given bindable, or {@code null} if
|
||||
* constructor binding is not supported.
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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
|
||||
*
|
||||
* https://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.bind;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to indicate which constructor to use when binding
|
||||
* configuration properties using constructor arguments rather than by calling setters. A
|
||||
* single parameterized constructor implicitly indicates that constructor binding should
|
||||
* be used unless the constructor is annotated with `@Autowired`.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Target({ ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ConstructorBinding {
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
@ -18,9 +18,13 @@ package org.springframework.boot.context.properties.bind;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default {@link BindConstructorProvider} implementation.
|
||||
@ -32,38 +36,140 @@ class DefaultBindConstructorProvider implements BindConstructorProvider {
|
||||
|
||||
@Override
|
||||
public Constructor<?> getBindConstructor(Bindable<?> bindable, boolean isNestedConstructorBinding) {
|
||||
Class<?> type = bindable.getType().resolve();
|
||||
if (bindable.getValue() != null || type == null) {
|
||||
return null;
|
||||
}
|
||||
if (KotlinDetector.isKotlinPresent() && KotlinDetector.isKotlinType(type)) {
|
||||
return getDeducedKotlinConstructor(type);
|
||||
}
|
||||
Constructor<?>[] constructors = type.getDeclaredConstructors();
|
||||
if (constructors.length == 1 && constructors[0].getParameterCount() > 0) {
|
||||
return constructors[0];
|
||||
}
|
||||
Constructor<?> constructor = null;
|
||||
for (Constructor<?> candidate : constructors) {
|
||||
if (!Modifier.isPrivate(candidate.getModifiers())) {
|
||||
if (constructor != null) {
|
||||
return null;
|
||||
}
|
||||
constructor = candidate;
|
||||
}
|
||||
}
|
||||
if (constructor != null && constructor.getParameterCount() > 0) {
|
||||
return constructor;
|
||||
}
|
||||
return null;
|
||||
return getBindConstructor(bindable.getType().resolve(), isNestedConstructorBinding);
|
||||
}
|
||||
|
||||
private Constructor<?> getDeducedKotlinConstructor(Class<?> type) {
|
||||
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(type);
|
||||
if (primaryConstructor != null && primaryConstructor.getParameterCount() > 0) {
|
||||
return primaryConstructor;
|
||||
@Override
|
||||
public Constructor<?> getBindConstructor(Class<?> type, boolean isNestedConstructorBinding) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
Constructors constructors = Constructors.getConstructors(type);
|
||||
if (constructors.getBind() != null || isNestedConstructorBinding) {
|
||||
Assert.state(!constructors.hasAutowired(),
|
||||
() -> type.getName() + " declares @ConstructorBinding and @Autowired constructor");
|
||||
}
|
||||
return constructors.getBind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data holder for autowired and bind constructors.
|
||||
*/
|
||||
static final class Constructors {
|
||||
|
||||
private final boolean hasAutowired;
|
||||
|
||||
private final Constructor<?> bind;
|
||||
|
||||
private Constructors(boolean hasAutowired, Constructor<?> bind) {
|
||||
this.hasAutowired = hasAutowired;
|
||||
this.bind = bind;
|
||||
}
|
||||
|
||||
boolean hasAutowired() {
|
||||
return this.hasAutowired;
|
||||
}
|
||||
|
||||
Constructor<?> getBind() {
|
||||
return this.bind;
|
||||
}
|
||||
|
||||
static Constructors getConstructors(Class<?> type) {
|
||||
Constructor<?>[] candidates = getCandidateConstructors(type);
|
||||
MergedAnnotations[] candidateAnnotations = getAnnotations(candidates);
|
||||
boolean hasAutowiredConstructor = isAutowiredPresent(candidateAnnotations);
|
||||
Constructor<?> bind = getConstructorBindingAnnotated(type, candidates, candidateAnnotations);
|
||||
if (bind == null && !hasAutowiredConstructor) {
|
||||
bind = deduceBindConstructor(candidates);
|
||||
}
|
||||
if (bind == null && !hasAutowiredConstructor && isKotlinType(type)) {
|
||||
bind = deduceKotlinBindConstructor(type);
|
||||
}
|
||||
return new Constructors(hasAutowiredConstructor, bind);
|
||||
}
|
||||
|
||||
private static Constructor<?>[] getCandidateConstructors(Class<?> type) {
|
||||
if (isInnerClass(type)) {
|
||||
return new Constructor<?>[0];
|
||||
}
|
||||
return Arrays.stream(type.getDeclaredConstructors())
|
||||
.filter((constructor) -> isNonSynthetic(constructor, type)).toArray(Constructor[]::new);
|
||||
}
|
||||
|
||||
private static boolean isInnerClass(Class<?> type) {
|
||||
try {
|
||||
return type.getDeclaredField("this$0").isSynthetic();
|
||||
}
|
||||
catch (NoSuchFieldException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNonSynthetic(Constructor<?> constructor, Class<?> type) {
|
||||
return !constructor.isSynthetic();
|
||||
}
|
||||
|
||||
private static MergedAnnotations[] getAnnotations(Constructor<?>[] candidates) {
|
||||
MergedAnnotations[] candidateAnnotations = new MergedAnnotations[candidates.length];
|
||||
for (int i = 0; i < candidates.length; i++) {
|
||||
candidateAnnotations[i] = MergedAnnotations.from(candidates[i]);
|
||||
}
|
||||
return candidateAnnotations;
|
||||
}
|
||||
|
||||
private static boolean isAutowiredPresent(MergedAnnotations[] candidateAnnotations) {
|
||||
for (MergedAnnotations annotations : candidateAnnotations) {
|
||||
if (annotations.isPresent(Autowired.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Constructor<?> getConstructorBindingAnnotated(Class<?> type, Constructor<?>[] candidates,
|
||||
MergedAnnotations[] mergedAnnotations) {
|
||||
Constructor<?> result = null;
|
||||
for (int i = 0; i < candidates.length; i++) {
|
||||
if (mergedAnnotations[i].isPresent(ConstructorBinding.class)) {
|
||||
Assert.state(candidates[i].getParameterCount() > 0,
|
||||
() -> type.getName() + " declares @ConstructorBinding on a no-args constructor");
|
||||
Assert.state(result == null,
|
||||
() -> type.getName() + " has more than one @ConstructorBinding constructor");
|
||||
result = candidates[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private static Constructor<?> deduceBindConstructor(Constructor<?>[] candidates) {
|
||||
if (candidates.length == 1 && candidates[0].getParameterCount() > 0) {
|
||||
return candidates[0];
|
||||
}
|
||||
Constructor<?> result = null;
|
||||
for (Constructor<?> candidate : candidates) {
|
||||
if (!Modifier.isPrivate(candidate.getModifiers())) {
|
||||
if (result != null) {
|
||||
return null;
|
||||
}
|
||||
result = candidate;
|
||||
}
|
||||
}
|
||||
return (result != null && result.getParameterCount() > 0) ? result : null;
|
||||
}
|
||||
|
||||
private static boolean isKotlinType(Class<?> type) {
|
||||
return KotlinDetector.isKotlinPresent() && KotlinDetector.isKotlinType(type);
|
||||
}
|
||||
|
||||
private static Constructor<?> deduceKotlinBindConstructor(Class<?> type) {
|
||||
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(type);
|
||||
if (primaryConstructor != null && primaryConstructor.getParameterCount() > 0) {
|
||||
return primaryConstructor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.context.properties.bind.ConstructorBinding;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
|
@ -27,7 +27,9 @@ import org.junit.jupiter.api.function.ThrowingConsumer;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesBean.BindMethod;
|
||||
import org.springframework.boot.context.properties.bind.BindConstructorProvider;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.ConstructorBinding;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -227,8 +229,26 @@ class ConfigurationPropertiesBeanTests {
|
||||
Bindable<?> target = propertiesBean.asBindTarget();
|
||||
assertThat(target.getType()).isEqualTo(ResolvableType.forClass(ConstructorBindingOnConstructor.class));
|
||||
assertThat(target.getValue()).isNull();
|
||||
assertThat(ConfigurationPropertiesBindConstructorProvider.INSTANCE
|
||||
.getBindConstructor(ConstructorBindingOnConstructor.class, false)).isNotNull();
|
||||
assertThat(BindConstructorProvider.DEFAULT.getBindConstructor(ConstructorBindingOnConstructor.class, false))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated(since = "3.0.0", forRemoval = true)
|
||||
void forValueObjectWithDeprecatedConstructorBindingAnnotatedClassReturnsBean() {
|
||||
ConfigurationPropertiesBean propertiesBean = ConfigurationPropertiesBean
|
||||
.forValueObject(DeprecatedConstructorBindingOnConstructor.class, "valueObjectBean");
|
||||
assertThat(propertiesBean.getName()).isEqualTo("valueObjectBean");
|
||||
assertThat(propertiesBean.getInstance()).isNull();
|
||||
assertThat(propertiesBean.getType()).isEqualTo(DeprecatedConstructorBindingOnConstructor.class);
|
||||
assertThat(propertiesBean.getBindMethod()).isEqualTo(BindMethod.VALUE_OBJECT);
|
||||
assertThat(propertiesBean.getAnnotation()).isNotNull();
|
||||
Bindable<?> target = propertiesBean.asBindTarget();
|
||||
assertThat(target.getType())
|
||||
.isEqualTo(ResolvableType.forClass(DeprecatedConstructorBindingOnConstructor.class));
|
||||
assertThat(target.getValue()).isNull();
|
||||
assertThat(BindConstructorProvider.DEFAULT.getBindConstructor(DeprecatedConstructorBindingOnConstructor.class,
|
||||
false)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -249,8 +269,8 @@ class ConfigurationPropertiesBeanTests {
|
||||
Bindable<?> target = propertiesBean.asBindTarget();
|
||||
assertThat(target.getType()).isEqualTo(ResolvableType.forClass(implicitConstructorBinding));
|
||||
assertThat(target.getValue()).isNull();
|
||||
Constructor<?> bindConstructor = ConfigurationPropertiesBindConstructorProvider.INSTANCE
|
||||
.getBindConstructor(implicitConstructorBinding, false);
|
||||
Constructor<?> bindConstructor = BindConstructorProvider.DEFAULT.getBindConstructor(implicitConstructorBinding,
|
||||
false);
|
||||
assertThat(bindConstructor).isNotNull();
|
||||
assertThat(bindConstructor.getParameterTypes()).containsExactly(String.class, Integer.class);
|
||||
}
|
||||
@ -268,64 +288,64 @@ class ConfigurationPropertiesBeanTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWhenNoConstructorBindingReturnsJavaBean() {
|
||||
BindMethod bindType = BindMethod.forType(NoConstructorBinding.class);
|
||||
void bindMethodGetWhenNoConstructorBindingReturnsJavaBean() {
|
||||
BindMethod bindType = BindMethod.get(NoConstructorBinding.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.JAVA_BEAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWhenConstructorBindingOnConstructorReturnsValueObject() {
|
||||
BindMethod bindType = BindMethod.forType(ConstructorBindingOnConstructor.class);
|
||||
void bindMethodGetWhenConstructorBindingOnConstructorReturnsValueObject() {
|
||||
BindMethod bindType = BindMethod.get(ConstructorBindingOnConstructor.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.VALUE_OBJECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWhenNoConstructorBindingAnnotationOnSingleParameterizedConstructorReturnsValueObject() {
|
||||
BindMethod bindType = BindMethod.forType(ConstructorBindingNoAnnotation.class);
|
||||
void bindMethodGetWhenNoConstructorBindingAnnotationOnSingleParameterizedConstructorReturnsValueObject() {
|
||||
BindMethod bindType = BindMethod.get(ConstructorBindingNoAnnotation.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.VALUE_OBJECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWhenConstructorBindingOnMultipleConstructorsThrowsException() {
|
||||
void bindMethodGetWhenConstructorBindingOnMultipleConstructorsThrowsException() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> BindMethod.forType(ConstructorBindingOnMultipleConstructors.class))
|
||||
.isThrownBy(() -> BindMethod.get(ConstructorBindingOnMultipleConstructors.class))
|
||||
.withMessage(ConstructorBindingOnMultipleConstructors.class.getName()
|
||||
+ " has more than one @ConstructorBinding constructor");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWithMultipleConstructorsReturnJavaBean() {
|
||||
BindMethod bindType = BindMethod.forType(NoConstructorBindingOnMultipleConstructors.class);
|
||||
void bindMethodGetWithMultipleConstructorsReturnJavaBean() {
|
||||
BindMethod bindType = BindMethod.get(NoConstructorBindingOnMultipleConstructors.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.JAVA_BEAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWithNoArgConstructorReturnsJavaBean() {
|
||||
BindMethod bindType = BindMethod.forType(JavaBeanWithNoArgConstructor.class);
|
||||
void bindMethodGetWithNoArgConstructorReturnsJavaBean() {
|
||||
BindMethod bindType = BindMethod.get(JavaBeanWithNoArgConstructor.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.JAVA_BEAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTypeForTypeWithSingleArgAutowiredConstructorReturnsJavaBean() {
|
||||
BindMethod bindType = BindMethod.forType(JavaBeanWithAutowiredConstructor.class);
|
||||
void bindMethodGetWithSingleArgAutowiredConstructorReturnsJavaBean() {
|
||||
BindMethod bindType = BindMethod.get(JavaBeanWithAutowiredConstructor.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.JAVA_BEAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorBindingAndAutowiredConstructorsShouldThrowException() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> BindMethod.forType(ConstructorBindingAndAutowiredConstructors.class));
|
||||
.isThrownBy(() -> BindMethod.get(ConstructorBindingAndAutowiredConstructors.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerClassWithSyntheticFieldShouldReturnJavaBean() {
|
||||
BindMethod bindType = BindMethod.forType(Inner.class);
|
||||
BindMethod bindType = BindMethod.get(Inner.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.JAVA_BEAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerClassWithParameterizedConstructorShouldReturnJavaBean() {
|
||||
BindMethod bindType = BindMethod.forType(ParameterizedConstructorInner.class);
|
||||
BindMethod bindType = BindMethod.get(ParameterizedConstructorInner.class);
|
||||
assertThat(bindType).isEqualTo(BindMethod.JAVA_BEAN);
|
||||
}
|
||||
|
||||
@ -533,6 +553,20 @@ class ConfigurationPropertiesBeanTests {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
@SuppressWarnings("removal")
|
||||
static class DeprecatedConstructorBindingOnConstructor {
|
||||
|
||||
DeprecatedConstructorBindingOnConstructor(String name) {
|
||||
this(name, -1);
|
||||
}
|
||||
|
||||
@org.springframework.boot.context.properties.ConstructorBinding
|
||||
DeprecatedConstructorBindingOnConstructor(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties
|
||||
static class ConstructorBindingOnMultipleConstructors {
|
||||
|
||||
|
@ -54,6 +54,7 @@ import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.properties.bind.BindException;
|
||||
import org.springframework.boot.context.properties.bind.ConstructorBinding;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
import org.springframework.boot.context.properties.bind.validation.BindValidationException;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||
|
@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.bind.ConstructorBinding;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
import org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2012-2022 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
|
||||
*
|
||||
* https://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.bind;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultBindConstructorProvider}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class DefaultBindConstructorProviderTests {
|
||||
|
||||
private DefaultBindConstructorProvider provider = new DefaultBindConstructorProvider();
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasOnlyDefaultConstructorReturnsNull() {
|
||||
Constructor<?> constructor = this.provider.getBindConstructor(OnlyDefaultConstructor.class, false);
|
||||
assertThat(constructor).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasMultipleAmbiguousConstructorsReturnsNull() {
|
||||
Constructor<?> constructor = this.provider.getBindConstructor(MultipleAmbiguousConstructors.class, false);
|
||||
assertThat(constructor).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasTwoConstructorsWithOneConstructorBindingReturnsConstructor() {
|
||||
Constructor<?> constructor = this.provider.getBindConstructor(TwoConstructorsWithOneConstructorBinding.class,
|
||||
false);
|
||||
assertThat(constructor).isNotNull();
|
||||
assertThat(constructor.getParameterCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasOneConstructorWithAutowiredReturnsNull() {
|
||||
Constructor<?> constructor = this.provider.getBindConstructor(OneConstructorWithAutowired.class, false);
|
||||
assertThat(constructor).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasTwoConstructorsWithOneAutowiredReturnsNull() {
|
||||
Constructor<?> constructor = this.provider.getBindConstructor(TwoConstructorsWithOneAutowired.class, false);
|
||||
assertThat(constructor).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasTwoConstructorsWithOneAutowiredAndOneConstructorBindingThrowsException() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.provider
|
||||
.getBindConstructor(TwoConstructorsWithOneAutowiredAndOneConstructorBinding.class, false))
|
||||
.withMessageContaining("declares @ConstructorBinding and @Autowired");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasOneConstructorWithConstructorBindingReturnsConstructor() {
|
||||
Constructor<?> constructor = this.provider.getBindConstructor(OneConstructorWithConstructorBinding.class,
|
||||
false);
|
||||
assertThat(constructor).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindConstructorWhenHasTwoConstructorsWithBothConstructorBindingThrowsException() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(
|
||||
() -> this.provider.getBindConstructor(TwoConstructorsWithBothConstructorBinding.class, false))
|
||||
.withMessageContaining("has more than one @ConstructorBinding");
|
||||
}
|
||||
|
||||
static class OnlyDefaultConstructor {
|
||||
|
||||
}
|
||||
|
||||
static class MultipleAmbiguousConstructors {
|
||||
|
||||
MultipleAmbiguousConstructors() {
|
||||
}
|
||||
|
||||
MultipleAmbiguousConstructors(String name) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TwoConstructorsWithOneConstructorBinding {
|
||||
|
||||
@ConstructorBinding
|
||||
TwoConstructorsWithOneConstructorBinding(String name) {
|
||||
this(name, 100);
|
||||
}
|
||||
|
||||
TwoConstructorsWithOneConstructorBinding(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class OneConstructorWithAutowired {
|
||||
|
||||
@Autowired
|
||||
OneConstructorWithAutowired(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TwoConstructorsWithOneAutowired {
|
||||
|
||||
@Autowired
|
||||
TwoConstructorsWithOneAutowired(String name) {
|
||||
this(name, 100);
|
||||
}
|
||||
|
||||
TwoConstructorsWithOneAutowired(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TwoConstructorsWithOneAutowiredAndOneConstructorBinding {
|
||||
|
||||
@Autowired
|
||||
TwoConstructorsWithOneAutowiredAndOneConstructorBinding(String name) {
|
||||
this(name, 100);
|
||||
}
|
||||
|
||||
@ConstructorBinding
|
||||
TwoConstructorsWithOneAutowiredAndOneConstructorBinding(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class OneConstructorWithConstructorBinding {
|
||||
|
||||
@ConstructorBinding
|
||||
OneConstructorWithConstructorBinding(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TwoConstructorsWithBothConstructorBinding {
|
||||
|
||||
@ConstructorBinding
|
||||
TwoConstructorsWithBothConstructorBinding(String name) {
|
||||
this(name, 100);
|
||||
}
|
||||
|
||||
@ConstructorBinding
|
||||
TwoConstructorsWithBothConstructorBinding(String name, int age) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.boot.context.properties.bind;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -366,7 +365,7 @@ class ValueObjectBinderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindToRecordWithDefaultValue() throws IOException {
|
||||
void bindToRecordWithDefaultValue() {
|
||||
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
|
||||
source.put("test.record.property1", "value-from-config-1");
|
||||
this.sources.add(source);
|
||||
|
@ -13,8 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
//
|
||||
package org.springframework.boot.context.properties;
|
||||
|
||||
package org.springframework.boot.context.properties.bind;
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalStateException
|
||||
@ -22,14 +22,14 @@ import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
|
||||
/**
|
||||
* Tests for `ConfigurationPropertiesBindConstructorProvider`.
|
||||
* Tests for `DefaultBindConstructorProvider`.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class ConfigurationPropertiesBindConstructorProviderTests {
|
||||
class KotlinDefaultBindConstructorProviderTests {
|
||||
|
||||
private val constructorProvider = ConfigurationPropertiesBindConstructorProvider()
|
||||
private val constructorProvider = DefaultBindConstructorProvider()
|
||||
|
||||
@Test
|
||||
fun `type with default constructor should register java bean`() {
|
||||
@ -119,66 +119,55 @@ class ConfigurationPropertiesBindConstructorProviderTests {
|
||||
assertThat(bindConstructor).isNotNull();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "foo")
|
||||
class FooProperties
|
||||
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
class PrimaryWithAutowiredSecondaryProperties constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
@Autowired
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String) : this(foo, 21)
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
class AutowiredSecondaryProperties {
|
||||
|
||||
@Autowired
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String)
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
class AutowiredPrimaryProperties @Autowired constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
class ConstructorBindingOnSecondaryAndAutowiredPrimaryProperties @Autowired constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
@ConstructorBinding
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String) : this(foo, 21)
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bar")
|
||||
class ConstructorBindingOnPrimaryAndAutowiredSecondaryProperties @ConstructorBinding constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
@Autowired
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String) : this(foo, 21)
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingOnSecondaryWithPrimaryConstructor constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
@ConstructorBinding
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String) : this(foo, 21)
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingOnPrimaryWithSecondaryConstructor @ConstructorBinding constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String) : this(foo, 21)
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingPrimaryConstructorNoAnnotation(val name: String?, val counter: Int = 42)
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingSecondaryConstructorNoAnnotation {
|
||||
|
||||
constructor(@Suppress("UNUSED_PARAMETER") foo: String)
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class MultipleAmbiguousConstructors {
|
||||
|
||||
constructor()
|
||||
@ -187,7 +176,6 @@ class ConfigurationPropertiesBindConstructorProviderTests {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingMultipleConstructors {
|
||||
|
||||
constructor(@Suppress("UNUSED_PARAMETER") bar: Int)
|
||||
@ -197,7 +185,6 @@ class ConfigurationPropertiesBindConstructorProviderTests {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingMultipleAnnotatedConstructors {
|
||||
|
||||
@ConstructorBinding
|
||||
@ -208,7 +195,6 @@ class ConfigurationPropertiesBindConstructorProviderTests {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
class ConstructorBindingSecondaryAndPrimaryAnnotatedConstructors @ConstructorBinding constructor(val name: String?, val counter: Int = 42) {
|
||||
|
||||
@ConstructorBinding
|
||||
@ -216,7 +202,6 @@ class ConfigurationPropertiesBindConstructorProviderTests {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "bing")
|
||||
data class ConstructorBindingDataClassWithDefaultValues(val name: String = "Joan", val counter: Int = 42)
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user