diff --git a/spring-boot-project/spring-boot-autoconfigure/pom.xml b/spring-boot-project/spring-boot-autoconfigure/pom.xml
index cd7633bb0e9..a187161806f 100755
--- a/spring-boot-project/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-project/spring-boot-autoconfigure/pom.xml
@@ -836,6 +836,16 @@
true
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+ test
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ test
+
org.springframework.boot
spring-boot-test-support
@@ -993,6 +1003,29 @@
test
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ ${project.basedir}/src/test/kotlin
+ ${project.basedir}/src/test/java
+
+
+
+
+
+
+
java9+
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java
index 2532aedcc7a..6d2fae82f7c 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/diagnostics/analyzer/NoSuchBeanDefinitionFailureAnalyzer.java
@@ -46,6 +46,7 @@ import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.diagnostics.analyzer.AbstractInjectionFailureAnalyzer;
import org.springframework.context.annotation.Bean;
+import org.springframework.core.KotlinDetector;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
@@ -120,10 +121,19 @@ class NoSuchBeanDefinitionFailureAnalyzer extends AbstractInjectionFailureAnalyz
MergedAnnotation configurationProperties = MergedAnnotations.from(declaringClass)
.get(ConfigurationProperties.class);
if (configurationProperties.isPresent()) {
- action = String.format(
- "%s%nConsider adding @%s to %s if you intended to use constructor-based "
- + "configuration property binding.",
- action, ConstructorBinding.class.getSimpleName(), constructor.getName());
+ if (KotlinDetector.isKotlinType(declaringClass) && !KotlinDetector.isKotlinReflectPresent()) {
+ action = String.format(
+ "%s%nConsider adding a dependency on kotlin-reflect so that the contructor used for @%s can be located. Also, ensure that @%s is present on '%s' if you intended to use constructor-based "
+ + "configuration property binding.",
+ action, ConstructorBinding.class.getSimpleName(), ConstructorBinding.class.getSimpleName(),
+ constructor.getName());
+ }
+ else {
+ action = String.format(
+ "%s%nConsider adding @%s to %s if you intended to use constructor-based "
+ + "configuration property binding.",
+ action, ConstructorBinding.class.getSimpleName(), constructor.getName());
+ }
}
}
return new FailureAnalysis(message.toString(), action, cause);
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/kotlin/org/springframework/boot/autoconfigure/diagnostics/analyzer/KotlinNoSuchBeanFailureAnalyzerNoKotlinReflectTests.kt b/spring-boot-project/spring-boot-autoconfigure/src/test/kotlin/org/springframework/boot/autoconfigure/diagnostics/analyzer/KotlinNoSuchBeanFailureAnalyzerNoKotlinReflectTests.kt
new file mode 100644
index 00000000000..e112ffa07bd
--- /dev/null
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/kotlin/org/springframework/boot/autoconfigure/diagnostics/analyzer/KotlinNoSuchBeanFailureAnalyzerNoKotlinReflectTests.kt
@@ -0,0 +1,67 @@
+package org.springframework.boot.autoconfigure.diagnostics.analyzer
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.springframework.beans.FatalBeanException
+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.diagnostics.FailureAnalysis
+import org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
+import org.springframework.boot.test.util.TestPropertyValues
+import org.springframework.boot.testsupport.classpath.ClassPathExclusions
+import org.springframework.context.annotation.AnnotationConfigApplicationContext
+import org.springframework.context.annotation.Configuration
+
+/**
+ * Tests for {@link ConfigurationProperties @ConfigurationProperties}-annotated beans when kotlin-reflect is not present
+ * on the classpath.
+ *
+ * @author Madhura Bhave
+ */
+@ClassPathExclusions("kotlin-reflect*.jar")
+class KotlinNoSuchBeanFailureAnalyzerNoKotlinReflectTests {
+
+ private val analyzer = NoSuchBeanDefinitionFailureAnalyzer()
+
+ @Test
+ fun failureAnalysisForConfigurationPropertiesThatMaybeShouldHaveBeenConstructorBound() {
+ val analysis = analyzeFailure(
+ createFailure(ConstructorBoundConfigurationPropertiesConfiguration::class.java))
+ assertThat(analysis!!.getAction()).startsWith(
+ java.lang.String.format("Consider defining a bean of type '%s' in your configuration.", String::class.java!!.getName()))
+ assertThat(analysis!!.getAction()).contains(java.lang.String.format(
+ "Consider adding a dependency on kotlin-reflect so that the contructor used for @ConstructorBinding can be located. Also, ensure that @ConstructorBinding is present on '%s' ", ConstructorBoundProperties::class.java!!.getName()))
+ }
+
+ private fun createFailure(config: Class<*>, vararg environment: String): FatalBeanException? {
+ try {
+ AnnotationConfigApplicationContext().use { context ->
+ this.analyzer.setBeanFactory(context.beanFactory)
+ TestPropertyValues.of(*environment).applyTo(context)
+ context.register(config)
+ context.refresh()
+ return null
+ }
+ } catch (ex: FatalBeanException) {
+ return ex
+ }
+
+ }
+
+ private fun analyzeFailure(failure: Exception?): FailureAnalysis? {
+ val analysis = this.analyzer.analyze(failure)
+ if (analysis != null) {
+ LoggingFailureAnalysisReporter().report(analysis)
+ }
+ return analysis
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @EnableConfigurationProperties(ConstructorBoundProperties::class)
+ internal class ConstructorBoundConfigurationPropertiesConfiguration
+
+ @ConfigurationProperties("test")
+ @ConstructorBinding
+ internal class ConstructorBoundProperties(val name: String)
+}
\ No newline at end of file