spring-projects / spring-projects/spring-framework

Avoid Kotlin reflection in MethodParameter for non-suspending functions

Open
#37,300 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

MethodParameter.getGenericParameterType() and getParameterType() resolve the return type of a Kotlin method (parameter index -1) through KotlinDelegate, which calls ReflectJvmMapping.getKotlinFunction(method) only to check KFunction.isSuspend(). That lookup materializes all members of the declaring KClass through Kotlin reflection and is repeated for every method whose return type is resolved, including the many non-suspending ones.

A suspending function always compiles to a JVM method whose last parameter is kotlin.coroutines.Continuation, which KotlinDetector.isSuspendingFunction(Method) already checks without Kotlin reflection and which the rest of the framework (AOP, caching, scheduling, messaging, ConstructorResolver) relies on. Guarding KotlinDelegate.getGenericReturnType(...) and getReturnType(...) with it keeps results identical (suspending functions still go through the KFunction path; a non-suspending function with an explicit trailing Continuation parameter is still verified via KFunction.isSuspend()) while regular Kotlin methods fall back to Java reflection immediately.

Measurements on reai.no, a large Spring Boot Kotlin application (Boot 4.2.0-SNAPSHOT / Framework 7.1.0-SNAPSHOT, JDK 27, Apple M5 Max):

main With guard
Cold-JVM return type resolution for the 1,418 methods of 337 Kotlin repository interfaces, 5 runs 1,584–2,766 ms 57–150 ms

In a wall-clock startup profile of that application (async-profiler, 2 ms sampling, extracted Boot layout, 243 Spring Data JPA repositories), MethodParameter$KotlinDelegate.getGenericReturnType / getReturnType accounted for 124 of 8,694 main-thread samples (1.4 % of startup), all below ReflectJvmMapping.getKotlinFunction; with the guard the frames no longer appear.

A signed-off implementation with two pinning tests in MethodParameterKotlinTests (green :spring-core:check on JDK 25 including the JDK 21/24 suites, CI green) is in #37299, whose fork I have since removed; the commit stays reachable at refs/pull/37299/head. We do not need authorship, please take it in whatever form suits the team. The equivalent guard for Spring Data Commons is tracked in spring-projects/spring-data-commons#3544. Related to #21546.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with MethodParameter’s KotlinDelegate methods and the existing MethodParameterKotlinTests mentioned in the issue. Inspect the reachable commit at refs/pull/37299/head, then run :spring-core:check, including the cited JDK suites. Done means the pinning tests pass and non-suspending return-type resolution avoids the Kotlin reflection lookup while suspending cases retain their existing path.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, kotlin
Domain
backend, performance
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.