Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility for mapping a property to an attribute with different name #39452

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -46,6 +46,7 @@
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Lasse Wulff
*/
class JavaBeanBinder implements DataObjectBinder {

Expand Down Expand Up @@ -92,7 +93,7 @@ private <T> boolean bind(DataObjectPropertyBinder propertyBinder, Bean<T> bean,

private <T> boolean bind(BeanSupplier<T> beanSupplier, DataObjectPropertyBinder propertyBinder,
BeanProperty property) {
String propertyName = property.getName();
String propertyName = determinePropertyName(property);
ResolvableType type = property.getType();
Supplier<Object> value = property.getValue(beanSupplier);
Annotation[] annotations = property.getAnnotations();
Expand All @@ -110,6 +111,15 @@ else if (value == null || !bound.equals(value.get())) {
return true;
}

private String determinePropertyName(BeanProperty property) {
return Arrays.stream((property.getAnnotations() != null) ? property.getAnnotations() : new Annotation[0])
.filter((annotation) -> annotation.annotationType() == Name.class)
.findFirst()
.map(Name.class::cast)
.map(Name::value)
.orElse(property.getName());
}

/**
* The properties of a bean that may be bound.
*/
Expand Down
Expand Up @@ -23,15 +23,16 @@
import java.lang.annotation.Target;

/**
* Annotation that can be used to specify the name when binding to an immutable property.
* This annotation may be required when binding to names that clash with reserved language
* Annotation that can be used to specify the name when binding to a property. This
* annotation may be required when binding to names that clash with reserved language
* keywords.
*
* @author Phillip Webb
* @author Lasse Wulff
* @since 2.4.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Documented
public @interface Name {

Expand Down
Expand Up @@ -52,6 +52,7 @@
* @author Phillip Webb
* @author Madhura Bhave
* @author Andy Wilkinson
* @author Lasse Wulff
*/
class JavaBeanBinderTests {

Expand All @@ -74,6 +75,27 @@ void bindToClassShouldCreateBoundBean() {
assertThat(bean.getEnumValue()).isEqualTo(ExampleEnum.FOO_BAR);
}

@Test
void bindRenamedPropertyToClassBean() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("renamed.public", "alpha");
this.sources.add(source);
ExampleRenamedPropertyBean bean = this.binder.bind("renamed", Bindable.of(ExampleRenamedPropertyBean.class))
.get();
assertThat(bean.getExampleProperty()).isEqualTo("alpha");
}

@Test
void bindRenamedPropertyToRecordBean() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
source.put("renamed.class", "alpha");
this.sources.add(source);
ExampleRenamedPropertyRecordBean bean = this.binder
.bind("renamed", Bindable.of(ExampleRenamedPropertyRecordBean.class))
.get();
assertThat(bean.exampleProperty()).isEqualTo("alpha");
}

@Test
void bindToClassWhenHasNoPrefixShouldCreateBoundBean() {
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
Expand Down Expand Up @@ -648,6 +670,24 @@ void setEnumValue(ExampleEnum enumValue) {

}

static class ExampleRenamedPropertyBean {

@Name("public")
private String exampleProperty;

String getExampleProperty() {
return this.exampleProperty;
}

void setExampleProperty(String exampleProperty) {
this.exampleProperty = exampleProperty;
}

}

record ExampleRenamedPropertyRecordBean(@Name("class") String exampleProperty) {
}

static class ExampleDefaultsBean {

private int foo = 123;
Expand Down
Expand Up @@ -26,11 +26,13 @@ import org.springframework.context.annotation.Import
import org.springframework.test.context.support.TestPropertySourceUtils

import org.assertj.core.api.Assertions.assertThat
import org.springframework.boot.context.properties.bind.Name

/**
* Tests for {@link ConfigurationProperties @ConfigurationProperties}-annotated beans.
*
* @author Madhura Bhave
* @author Lasse Wulff
*/
class KotlinConfigurationPropertiesTests {

Expand Down Expand Up @@ -59,6 +61,22 @@ class KotlinConfigurationPropertiesTests {
assertThat(this.context.getBean(LateInitProperties::class.java).inner.value).isEqualTo("alpha")
}

@Test
fun `renamed property can be bound`() {
this.context.register(EnableRenamedProperties::class.java)
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "renamed.fun=beta")
this.context.refresh()
assertThat(this.context.getBean(RenamedProperties::class.java).bar).isEqualTo("beta")
}

@Test
fun `renamed property can be bound to late init attribute`() {
this.context.register(EnableRenamedLateInitProperties::class.java)
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "renamed.var=beta")
this.context.refresh()
assertThat(this.context.getBean(RenamedLateInitProperties::class.java).bar).isEqualTo("beta")
}

@Test
fun `type with constructor bound lateinit property with default can be bound`() {
this.context.register(EnableLateInitPropertiesWithDefault::class.java)
Expand All @@ -80,6 +98,21 @@ class KotlinConfigurationPropertiesTests {
@ConfigurationProperties(prefix = "foo")
class BingProperties(@Suppress("UNUSED_PARAMETER") bar: String)

@ConfigurationProperties(prefix = "renamed")
class RenamedProperties(@Name("fun") val bar: String)

@EnableConfigurationProperties(RenamedProperties::class)
class EnableRenamedProperties

@ConfigurationProperties(prefix = "renamed")
class RenamedLateInitProperties{
@Name("var")
lateinit var bar: String
}

@EnableConfigurationProperties(RenamedLateInitProperties::class)
class EnableRenamedLateInitProperties

@EnableConfigurationProperties
class EnableConfigProperties

Expand Down