Skip to content

Commit 89fca1b

Browse files
committed
Fix Kotlin inner class nested configuration handling
Before this commit, Kotlin inner class nested configuration handling thrown an IndexOutOfBoundsException due to bogus filtering of its constructor parameter reference to an instance of the outer class. This commit keep constructor parameter of type INSTANCE in order to throw a more meaningful NoSuchBeanDefinitionException. Issue: SPR-17222
1 parent 92bb76f commit 89fca1b

File tree

3 files changed

+50
-23
lines changed

3 files changed

+50
-23
lines changed

spring-core/src/main/java/org/springframework/core/MethodParameter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.List;
3030
import java.util.Map;
3131
import java.util.Optional;
32+
import java.util.function.Predicate;
3233
import java.util.stream.Collectors;
3334

3435
import kotlin.reflect.KFunction;
@@ -759,17 +760,21 @@ public static boolean isOptional(MethodParameter param) {
759760
}
760761
else {
761762
KFunction<?> function = null;
763+
Predicate<KParameter> predicate = null;
762764
if (method != null) {
763765
function = ReflectJvmMapping.getKotlinFunction(method);
766+
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind());
764767
}
765768
else if (ctor != null) {
766769
function = ReflectJvmMapping.getKotlinFunction(ctor);
770+
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()) ||
771+
KParameter.Kind.INSTANCE.equals(p.getKind());
767772
}
768773
if (function != null) {
769774
List<KParameter> parameters = function.getParameters();
770775
KParameter parameter = parameters
771776
.stream()
772-
.filter(p -> KParameter.Kind.VALUE.equals(p.getKind()))
777+
.filter(predicate)
773778
.collect(Collectors.toList())
774779
.get(index);
775780
return (parameter.getType().isMarkedNullable() || parameter.isOptional());
Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,15 +13,11 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
1716
package org.springframework.core
1817

19-
import java.lang.reflect.Method
20-
21-
import org.junit.Before
2218
import org.junit.Test
23-
2419
import org.junit.Assert.*
20+
import java.lang.reflect.Method
2521

2622
/**
2723
* Tests for Kotlin support in [MethodParameter].
@@ -32,36 +28,58 @@ import org.junit.Assert.*
3228
*/
3329
class KotlinMethodParameterTests {
3430

35-
lateinit var nullableMethod: Method
31+
private val nullableMethod: Method = javaClass.getMethod("nullable", String::class.java)
3632

37-
lateinit var nonNullableMethod: Method
33+
private val nonNullableMethod = javaClass.getMethod("nonNullable", String::class.java)
3834

35+
private val innerClassConstructor = InnerClass::class.java.getConstructor(KotlinMethodParameterTests::class.java)
3936

40-
@Before
41-
@Throws(NoSuchMethodException::class)
42-
fun setup() {
43-
nullableMethod = javaClass.getMethod("nullable", String::class.java)
44-
nonNullableMethod = javaClass.getMethod("nonNullable", String::class.java)
45-
}
37+
private val innerClassWithParametersConstructor = InnerClassWithParameter::class.java
38+
.getConstructor(KotlinMethodParameterTests::class.java, String::class.java, String::class.java)
39+
40+
private val regularClassConstructor = RegularClass::class.java.getConstructor(String::class.java, String::class.java)
4641

4742

4843
@Test
4944
fun `Method parameter nullability`() {
50-
assertTrue(MethodParameter(nullableMethod, 0).isOptional())
51-
assertFalse(MethodParameter(nonNullableMethod, 0).isOptional())
45+
assertTrue(MethodParameter(nullableMethod, 0).isOptional)
46+
assertFalse(MethodParameter(nonNullableMethod, 0).isOptional)
5247
}
5348

5449
@Test
5550
fun `Method return type nullability`() {
56-
assertTrue(MethodParameter(nullableMethod, -1).isOptional())
57-
assertFalse(MethodParameter(nonNullableMethod, -1).isOptional())
51+
assertTrue(MethodParameter(nullableMethod, -1).isOptional)
52+
assertFalse(MethodParameter(nonNullableMethod, -1).isOptional)
53+
}
54+
55+
@Test // SPR-17222
56+
fun `Inner class constructor`() {
57+
assertFalse(MethodParameter(innerClassConstructor, 0).isOptional)
58+
59+
assertFalse(MethodParameter(innerClassWithParametersConstructor, 0).isOptional)
60+
assertFalse(MethodParameter(innerClassWithParametersConstructor, 1).isOptional)
61+
assertTrue(MethodParameter(innerClassWithParametersConstructor, 2).isOptional)
62+
}
63+
64+
@Test
65+
fun `Regular class constructor`() {
66+
assertFalse(MethodParameter(regularClassConstructor, 0).isOptional)
67+
assertTrue(MethodParameter(regularClassConstructor, 1).isOptional)
5868
}
5969

6070

61-
@Suppress("unused", "unused_parameter")
62-
fun nullable(p1: String?): Int? = 42
71+
@Suppress("unused_parameter")
72+
fun nullable(nullable: String?): Int? = 42
73+
74+
@Suppress("unused_parameter")
75+
fun nonNullable(nonNullable: String): Int = 42
76+
77+
inner class InnerClass
78+
79+
@Suppress("unused_parameter")
80+
inner class InnerClassWithParameter(nonNullable: String, nullable: String?)
6381

64-
@Suppress("unused", "unused_parameter")
65-
fun nonNullable(p1: String): Int = 42
82+
@Suppress("unused_parameter")
83+
class RegularClass(nonNullable: String, nullable: String?)
6684

6785
}

src/docs/asciidoc/languages/kotlin.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ for serializing / deserializing JSON data is automatically registered when
143143
found in the classpath and a warning message will be logged if Jackson and Kotlin are
144144
detected without the Jackson Kotlin module present.
145145

146+
Configuration classes can be
147+
https://kotlinlang.org/docs/reference/nested-classes.html[top level or nested but not inner]
148+
since the later requires a reference to the outer class.
149+
146150

147151

148152

0 commit comments

Comments
 (0)