BindingReflectionHintsRegistrar currently does not add reflection entries for Kotlin data class components.

With this kind of repository and data class:

data class Customer(@Id val id: Int?, val name: String)
interface CustomerRepository : CoroutineCrudRepository<Customer, Int>

The following error is visible at startup:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository': Could not compute caller for function: public final operator fun component1(): kotlin.Int? defined in com.example.demo.Customer[DeserializedSimpleFunctionDescriptor@32208e3c] (member = null)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1753) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1375) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1295) ~[demo:6.0.0-RC1]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:348) ~[na:na]
        ... 21 common frames omitted
Caused by: kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: public final operator fun component1(): kotlin.Int? defined in com.example.demo.Customer[DeserializedSimpleFunctionDescriptor@32208e3c] (member = null)
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:88) ~[na:na]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61) ~[na:na]
        at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63) ~[na:na]
        at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32) ~[demo:1.7.20-release-201(1.7.20)]
        at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61) ~[na:na]
        at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63) ~[na:na]
        at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:136) ~[na:na]
        at org.springframework.core.MethodParameter$KotlinDelegate.getGenericReturnType(MethodParameter.java:914) ~[na:na]
        at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:510) ~[demo:6.0.0-RC1]
        at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:291) ~[na:na]
        at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:107) ~[na:na]
        at org.springframework.core.ResolvableType.forType(ResolvableType.java:1413) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) ~[demo:6.0.0-RC1]
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1243) ~[demo:6.0.0-RC1]
        at org.springframework.core.GenericTypeResolver.resolveReturnType(GenericTypeResolver.java:80) ~[na:na]
        at org.springframework.beans.GenericTypeAwarePropertyDescriptor.<init>(GenericTypeAwarePropertyDescriptor.java:110) ~[na:na]
        at org.springframework.beans.CachedIntrospectionResults.buildGenericTypeAwarePropertyDescriptor(CachedIntrospectionResults.java:431) ~[na:na]
        at org.springframework.beans.CachedIntrospectionResults.<init>(CachedIntrospectionResults.java:310) ~[na:na]
        at org.springframework.beans.CachedIntrospectionResults.forClass(CachedIntrospectionResults.java:183) ~[na:na]
        at org.springframework.beans.BeanUtils.getPropertyDescriptors(BeanUtils.java:489) ~[na:na]
        at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:414) ~[demo:3.0.0-SNAPSHOT]

It should be tested in https://github.com/spring-projects/spring-aot-smoke-tests and work out of the box.

Comment From: cmdjulian

I also did encounter the problem for a for two other situations:

Mind the following controller:

@RestController
@RequestMapping(NOTE_ENDPOINT)
class NoteController(private val service: NoteService) {

    @PostMapping
    fun createNote(@PathVariable notebookId: UUID, @Valid @RequestBody command: NoteCommand): NoteDto {
        return service.createNote(command.toNote(notebookId), command.tags).toDto()
    }

    @GetMapping
    fun findNotes(@PathVariable notebookId: UUID, filters: NoteFilters): List<NoteDto> {
        return service.findNotes(notebookId, filters.tagNames, filters.content).map(Note::toDto)
    }
}

data class NoteCommand(

    @get:Size(min = 3, max = 128)
    val title: String,

    @get:Size(max = 65536)
    val content: String?,

    val tags: List<String> = emptyList()

)

data class NoteFilters(val tagNames: List<String>?, val content: String?)

Sending a valid NoteCommand works as expected. But if some of the constraints are violated, an error is raised resulting in a 500 server error, as the previously described problem is triggered as well.

Secondly, requesting the collection endpoint, having a container object for a bunch of request parameters, in my case NoteFilters, also causes the application to respond with a 500 server error because of missing reflection config.

Bot can be prevented by registering the aforementioned types in the reflect config, something like that works:

class AotConfig : RuntimeHintsRegistrar {
    override fun registerHints(hints: RuntimeHints, classLoader: ClassLoader?) {
        hints.reflection().registerType(NoteCommand::class.java, *MemberCategory.values())
        hints.reflection().registerType(NoteFilters::class.java, *MemberCategory.values())
    }
}

with src/main/resources/META-INF/spring:

org.springframework.aot.hint.RuntimeHintsRegistrar=com.example.app.config.AotConfig

Comment From: sdeleuze

@cmdjulian Could be a different issue. I have created https://github.com/spring-projects/spring-framework/issues/29331 for something I have been able to reproduce. Please create another issue if you see other problems and please add a repro prject + the exception you see.

Comment From: cmdjulian

Okay, will do, thanks!