KotlinReflectionUtils.isSuspend(Method) resolves the KFunction for every Kotlin method it is asked about via ReflectJvmMapping.getKotlinFunction(method). That lookup materializes all members of the declaring KClass through Kotlin reflection (metadata deserialization plus a linear scan comparing javaMethod for each member), only to read KFunction.isSuspend().
AbstractRepositoryMetadata.getReturnType(Method) calls isSuspend for every query method during repository initialization, and RepositoryMethodInvoker calls it again when the invoker is created. For Kotlin repositories that do not use coroutines at all, this is pure overhead: a suspend function always compiles to a JVM method whose last parameter is kotlin.coroutines.Continuation, so a method without such a parameter can never be suspending. Spring Framework's KotlinDetector.isSuspendingFunction(Method) performs exactly that check without loading Kotlin reflection, and QueryExecutionResultHandler in this module already relies on it.
In a Spring Boot 4.2.0-SNAPSHOT / Spring Data 2026.1.0-SNAPSHOT Kotlin application with 243 JPA repositories and 1,418 non-synthetic repository interface methods, a wall-clock startup profile (async-profiler, 2 ms sampling, extracted Boot layout) attributed 241 of 8,694 main-thread samples (2.8 % of startup, about 0.5 s) to KotlinReflectionUtils.isSuspend, all of it below ReflectJvmMapping.getKotlinFunction. A cold-JVM micro-benchmark that calls isSuspend for those 1,418 methods takes 1,146–1,341 ms on current main and 3–4 ms when the Continuation parameter check runs first (5 runs each, JDK 27, Apple M5 Max).
Proposal: short-circuit isSuspend with KotlinDetector.isSuspendingFunction(method) before touching Kotlin reflection, keeping the KFunction.isSuspend() verification for methods that do declare a trailing Continuation parameter so the result stays identical for non-suspending functions that take a Continuation argument explicitly.
KotlinReflectionUtils.isSuspend(Method)resolves theKFunctionfor every Kotlin method it is asked about viaReflectJvmMapping.getKotlinFunction(method). That lookup materializes all members of the declaringKClassthrough Kotlin reflection (metadata deserialization plus a linear scan comparingjavaMethodfor each member), only to readKFunction.isSuspend().AbstractRepositoryMetadata.getReturnType(Method)callsisSuspendfor every query method during repository initialization, andRepositoryMethodInvokercalls it again when the invoker is created. For Kotlin repositories that do not use coroutines at all, this is pure overhead: asuspendfunction always compiles to a JVM method whose last parameter iskotlin.coroutines.Continuation, so a method without such a parameter can never be suspending. Spring Framework'sKotlinDetector.isSuspendingFunction(Method)performs exactly that check without loading Kotlin reflection, andQueryExecutionResultHandlerin this module already relies on it.In a Spring Boot 4.2.0-SNAPSHOT / Spring Data 2026.1.0-SNAPSHOT Kotlin application with 243 JPA repositories and 1,418 non-synthetic repository interface methods, a wall-clock startup profile (async-profiler, 2 ms sampling, extracted Boot layout) attributed 241 of 8,694 main-thread samples (2.8 % of startup, about 0.5 s) to
KotlinReflectionUtils.isSuspend, all of it belowReflectJvmMapping.getKotlinFunction. A cold-JVM micro-benchmark that callsisSuspendfor those 1,418 methods takes 1,146–1,341 ms on currentmainand 3–4 ms when theContinuationparameter check runs first (5 runs each, JDK 27, Apple M5 Max).Proposal: short-circuit
isSuspendwithKotlinDetector.isSuspendingFunction(method)before touching Kotlin reflection, keeping theKFunction.isSuspend()verification for methods that do declare a trailingContinuationparameter so the result stays identical for non-suspending functions that take aContinuationargument explicitly.