Fix constructor binding to Kotlin data class with default values

Closes gh-32416
This commit is contained in:
Andy Wilkinson 2022-09-21 21:34:34 +01:00
parent f9c341c75a
commit 6b8575b001
2 changed files with 27 additions and 1 deletions

View File

@ -19,9 +19,11 @@ 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;
@ -94,6 +96,9 @@ public class ConfigurationPropertiesBindConstructorProvider implements BindConst
}
bind = findAnnotatedConstructor(type, bind, candidate);
}
if (bind == null && !hasAutowiredConstructor && isKotlinType(type)) {
bind = deduceKotlinBindConstructor(type);
}
return new Constructors(hasAutowiredConstructor, bind);
}
@ -141,6 +146,18 @@ public class ConfigurationPropertiesBindConstructorProvider implements BindConst
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;
}
}
}

View File

@ -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.
@ -113,6 +113,12 @@ class ConfigurationPropertiesBindConstructorProviderTests {
}
}
@Test
fun `data class with default values should use constructor binding`() {
val bindConstructor = this.constructorProvider.getBindConstructor(ConstructorBindingDataClassWithDefaultValues::class.java, false)
assertThat(bindConstructor).isNotNull();
}
@ConfigurationProperties(prefix = "foo")
class FooProperties
@ -210,4 +216,7 @@ class ConfigurationPropertiesBindConstructorProviderTests {
}
@ConfigurationProperties(prefix = "bing")
data class ConstructorBindingDataClassWithDefaultValues(val name: String = "Joan", val counter: Int = 42)
}