Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Configuration classes with Kotlin in native-image are broken #29663

Closed
mhalbritter opened this issue Dec 8, 2022 · 2 comments
Closed

@Configuration classes with Kotlin in native-image are broken #29663

mhalbritter opened this issue Dec 8, 2022 · 2 comments
Assignees
Labels
theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support type: enhancement A general enhancement
Milestone

Comments

@mhalbritter
Copy link
Contributor

mhalbritter commented Dec 8, 2022

This happens only in a native image. I have this configuration:

@Configuration(proxyBeanMethods = false)
class MyConfiguration {
    @Bean
    fun a(): A {
        return A()
    }

    private fun b(): B {
        return B()
    }
}

class A

class B

Running the application works. Now rename the a() method to z_a() and it breaks:

java.lang.ClassNotFoundException: com.example.demo.B
        at java.base@17.0.5/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52) ~[demo:na]
        at java.base@17.0.5/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
        at java.base@17.0.5/java.lang.ClassLoader.loadClass(ClassLoader.java:132) ~[demo:na]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:273) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadReturnType(KDeclarationContainerImpl.kt:288) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:198) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68) ~[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.21-release-272(1.7.21)]
        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) ~[na:na]
        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.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681) ~[demo:6.0.2]
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559) ~[demo:6.0.2]
        at org.springframework.util.ReflectionUtils.rethrowRuntimeException(ReflectionUtils.java:147)
        at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:794)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
        at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13)
Caused by: java.lang.ClassNotFoundException: com.example.demo.B
        at java.base@17.0.5/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52)
        at java.base@17.0.5/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base@17.0.5/java.lang.ClassLoader.loadClass(ClassLoader.java:132)
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:273)
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadReturnType(KDeclarationContainerImpl.kt:288)
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:198)
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531) ~[demo:6.0.2]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:106) ~[na:na]
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745) ~[demo:6.0.2]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565) ~[demo:6.0.2]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[demo:3.0.0]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:61)
        at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:63)
        at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
        at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:61)
        at kotlin.reflect.jvm.ReflectJvmMapping.getJavaMethod(ReflectJvmMapping.kt:63)
        at kotlin.reflect.jvm.ReflectJvmMapping.getKotlinFunction(ReflectJvmMapping.kt:136)
        at org.springframework.core.MethodParameter$KotlinDelegate.getGenericReturnType(MethodParameter.java:914)
        at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:510)
        at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:291)
        at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:107)
        at org.springframework.core.ResolvableType.forType(ResolvableType.java:1413)
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334)
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316)
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283)
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652)
        at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531)
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:106)
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:745)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
        ... 3 more
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[demo:3.0.0]
        at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13) ~[demo:na]

2022-12-08T17:30:40.001+01:00  WARN 179946 --- [           main] o.s.boot.SpringApplication               : Unable to close ApplicationContext

java.lang.ClassNotFoundException: com.example.demo.B
        at java.base@17.0.5/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:52) ~[demo:na]
        at java.base@17.0.5/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
        at java.base@17.0.5/java.lang.ClassLoader.loadClass(ClassLoader.java:132) ~[demo:na]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.parseType(KDeclarationContainerImpl.kt:273) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.loadReturnType(KDeclarationContainerImpl.kt:288) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KDeclarationContainerImpl.findMethodBySignature(KDeclarationContainerImpl.kt:198) ~[demo:1.7.21-release-272(1.7.21)]
        at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:68) ~[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.21-release-272(1.7.21)]
        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) ~[na:na]
        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.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1334) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1316) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1283) ~[demo:6.0.2]
        at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1228) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:814) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:681) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:652) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1632) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:559) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:531) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:658) ~[demo:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:650) ~[demo:6.0.2]
        at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1276) ~[demo:6.0.2]
        at org.springframework.boot.SpringApplication.getExitCodeFromMappedException(SpringApplication.java:863) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.getExitCodeFromException(SpringApplication.java:851) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.handleExitCode(SpringApplication.java:838) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:778) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[demo:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[demo:3.0.0]
        at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:13) ~[demo:na]

Demo project here: demo.zip

I debugged that to a behaviour in Kotlin reflection. Essentially you need reflection metadata for ALL methods (not only for the @Bean methods like it's at the moment) if you use ResolvableType.forMethodReturnType(method). It depends on the iteration order of the methods. You can read more here: https://github.com/mhalbritter/kotlin-graalvm-reflection

@mhalbritter mhalbritter changed the title @Configuration classes with Kotlin are broken @Configuration classes with Kotlin in native-image are broken Dec 8, 2022
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 8, 2022
@sbrannen sbrannen added theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support labels Dec 8, 2022
@sbrannen sbrannen added this to the Triage Queue milestone Dec 8, 2022
@sdeleuze sdeleuze self-assigned this Jan 2, 2023
@sdeleuze sdeleuze removed the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 2, 2023
@sdeleuze sdeleuze modified the milestones: Triage Queue, 6.0.x Jan 2, 2023
@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 2, 2023

Confirmed, I have bring this behavior to the attention of the Kotlin team and will share their feedback here. Worst case scenario, I guess we could configure introspect medadata for all functions of Kotlin classes.

@sdeleuze sdeleuze modified the milestones: 6.0.x, 6.0.4 Jan 2, 2023
@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 9, 2023

Feedback from Kotlin team:

The implementation can be optimized somewhat, but in the worst case we still have to iterate over all methods in a class, because the rules of correspondence between KFunction <-> java.lang.reflect.Method are not trivial and even their names don't match sometimes (e.g. because of @JvmName, default parameters, inline classes, etc).

So let's configure introspection on all functions for Kotlin classes.

sdeleuze added a commit to sdeleuze/spring-framework that referenced this issue Jan 10, 2023
Kotlin reflection API invocation on a specific function
may require iterating on all Java methods to find the right
Kotlin function. As a consequence, this commit adds introspection
hints on the class declared methods since the impact on the
footprint is low.

Closes spring-projectsgh-29663
sdeleuze added a commit to sdeleuze/spring-framework that referenced this issue Jan 10, 2023
Kotlin reflection API invocation on a specific function
may require iterating on all Java methods to find the right
Kotlin function. As a consequence, this commit adds introspection
hints on the class declared methods for all Kotlin beans since
the impact on the footprint is low.

Closes spring-projectsgh-29663
@sdeleuze sdeleuze added the type: enhancement A general enhancement label Jan 10, 2023
sdeleuze added a commit to sdeleuze/spring-framework that referenced this issue Jan 10, 2023
Kotlin reflection API invocation on a specific function
may require iterating on all Java methods to find the right
Kotlin function. As a consequence, this commit adds introspection
hints on the class declared methods for all Kotlin beans since
the impact on the footprint is low.

Closes spring-projectsgh-29663
mdeinum pushed a commit to mdeinum/spring-framework that referenced this issue Jun 29, 2023
Kotlin reflection API invocation on a specific function
may require iterating on all Java methods to find the right
Kotlin function. As a consequence, this commit adds introspection
hints on the class declared methods for all Kotlin beans since
the impact on the footprint is low.

Closes spring-projectsgh-29663
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-of-time processing theme: kotlin An issue related to Kotlin support type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants