Skip to content

Commit d41ca09

Browse files
committed
Add native support for Kotlin default values in controllers
Closes gh-33384
1 parent 143736e commit d41ca09

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

spring-web/src/main/java/org/springframework/web/bind/annotation/ControllerMappingReflectiveProcessor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
import org.springframework.aot.hint.ExecutableMode;
2626
import org.springframework.aot.hint.ReflectionHints;
2727
import org.springframework.aot.hint.annotation.ReflectiveProcessor;
28+
import org.springframework.core.KotlinDetector;
2829
import org.springframework.core.MethodParameter;
2930
import org.springframework.core.annotation.AnnotatedElementUtils;
3031
import org.springframework.http.HttpEntity;
3132
import org.springframework.lang.Nullable;
3233
import org.springframework.stereotype.Controller;
34+
import org.springframework.util.ReflectionUtils;
3335

3436
/**
3537
* {@link ReflectiveProcessor} implementation for {@link Controller} and
@@ -71,6 +73,11 @@ protected void registerTypeHints(ReflectionHints hints, Class<?> type) {
7173

7274
protected void registerMethodHints(ReflectionHints hints, Method method) {
7375
hints.registerMethod(method, ExecutableMode.INVOKE);
76+
Class<?> declaringClass = method.getDeclaringClass();
77+
if (KotlinDetector.isKotlinType(declaringClass)) {
78+
ReflectionUtils.doWithMethods(declaringClass, m -> hints.registerMethod(m, ExecutableMode.INVOKE),
79+
m -> m.getName().equals(method.getName() + "$default"));
80+
}
7481
for (Parameter parameter : method.getParameters()) {
7582
registerParameterTypeHints(hints, MethodParameter.forParameter(parameter));
7683
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.bind.annotation
18+
19+
import org.assertj.core.api.Assertions
20+
import org.junit.jupiter.api.Test
21+
import org.springframework.aot.hint.*
22+
23+
/**
24+
* Kotlin tests for {@link ControllerMappingReflectiveProcessor}.
25+
*
26+
* @author Sebastien Deleuze
27+
*/
28+
class ControllerMappingReflectiveProcessorKotlinTests {
29+
30+
private val processor = ControllerMappingReflectiveProcessor()
31+
32+
private val hints = ReflectionHints()
33+
34+
@Test
35+
fun registerReflectiveHintsForFunctionWithDefaultArgumentValue() {
36+
val method = SampleController::class.java.getDeclaredMethod("defaultValue", Boolean::class.javaObjectType)
37+
processor.registerReflectionHints(hints, method)
38+
Assertions.assertThat(hints.typeHints()).satisfiesExactlyInAnyOrder(
39+
{
40+
Assertions.assertThat(it.type).isEqualTo(TypeReference.of(SampleController::class.java))
41+
Assertions.assertThat(it.methods()).extracting<String> { executableHint: ExecutableHint -> executableHint.name }
42+
.containsExactlyInAnyOrder("defaultValue", "defaultValue\$default")
43+
}
44+
)
45+
}
46+
47+
class SampleController {
48+
49+
@GetMapping("/defaultValue")
50+
fun defaultValue(@RequestParam(required = false) argument: Boolean? = false) = argument
51+
}
52+
53+
}

0 commit comments

Comments
 (0)