Skip to content

Suspend handler methods fail on nullable value class parameters #32353

@efemoney

Description

@efemoney

Affects: Spring Framework 6.1.4, Spring Boot 3.2.3


This is related to support added in #27345 for value classes. This is because it tries to box an already boxed value class.


Take for example this handler method:

@JvmInline value class SomeId(val int: Int)

@GetMapping("/path")
suspend fun handle(
  @RequestParam nonNull: SomeId,
  @RequestParam nullable: SomeId?,
)

Calling /path?nonNull=1&nullable=2 will fail because the type of the nullable method parameter is SomeId? and Spring cannot find a Converter from String -> SomeId.

If we instead add a custom converter from String -> SomeId, the same endpoint will still fail because now the resolved argument has type SomeId but invokeSuspendingFunction wants to box it because its a value class.

My current workaround is to override invokeSuspendingFunction with my own variant that checks the arg[index] type first before boxing.


Adding excerpt from my own code comments:

Because of the way value classes work on the jvm, the runtime java reflection types of both parameters,
and hence the type that the spring handler method arguments have are Class<Int> & Class<SomeId> respectively.
These are the types used to [resolve arguments][HandlerMethodArgumentResolver.resolveArgument] and
more importantly, to convert from request param, request header etc.

(In our case, since value classes are not handled by default, we configure a special [GenericConverter]
that will convert into a value class type from request params, headers etc.)

However, run time kotlin reflection types for both parameters are KClass<SomeId> && KClass<SomeId?>
and these are the types used in [CoroutinesUtils.invokeSuspendingFunction] to determine whether
to box the converted argument or not.

This argument could be of wrapped type or value class type
depending on whether the declaration is nullable or not and boxing will fail on the latter!

This method is a kotlin port of [CoroutinesUtils.invokeSuspendingFunction] that checks
if the instance type is the value class just before deciding whether to box or not

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)theme: kotlinAn issue related to Kotlin supporttype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions