Motivation
In AOT mode, if the init/destroy methods are private and auto-discovered thanks to an annotation, the generated code sets the init and destroy method names with the fully qualified name of the declaring class as prefix to prevent naming conflicts which causes an error of the following type when starting the application (native image or with -Dspring.aot.enabled=true
):
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoBean': Could not find an init method named 'com.example.demo.DemoBean.init' on bean with name 'demoBean'
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1770) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:598) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:941) ~[spring-context-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608) ~[spring-context-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733) ~[spring-boot-3.2.0-SNAPSHOT.jar!/:3.2.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435) ~[spring-boot-3.2.0-SNAPSHOT.jar!/:3.2.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-3.2.0-SNAPSHOT.jar!/:3.2.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1305) ~[spring-boot-3.2.0-SNAPSHOT.jar!/:3.2.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1294) ~[spring-boot-3.2.0-SNAPSHOT.jar!/:3.2.0-SNAPSHOT]
at com.example.demo.DemoApplication.main(DemoApplication.java:10) ~[classes!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:95) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.beans.factory.support.BeanDefinitionValidationException: Could not find an init method named 'com.example.demo.DemoBean.init' on bean with name 'demoBean'
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1849) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1826) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766) ~[spring-beans-6.0.10-SNAPSHOT.jar!/:6.0.10-SNAPSHOT]
Steps to reproduce: * Create a demo project from https://start.spring.io/ with "GraalVM Native Support" as the only dependency * Add the following Configuration and Bean classes
Configuration class:
@Configuration
public class DemoConfiguration {
@Bean
public DemoBean demoBean() {
return new DemoBean();
}
}
Bean class:
public class DemoBean {
@PostConstruct
private void init() {
System.out.println("Init DemoBean");
}
@PreDestroy
private void destroy() {
System.out.println("Destroy DemoBean");
}
}
Modifications:
- Uses the name of the method instead of the identifier of the
LifecycleElement
even if it won't solve the potential naming conflict but it is a more specific use case - Adds a unit test to illustrate the use case
Result
The init and destroy methods are called when starting the application (native image or with -Dspring.aot.enabled=true
).
Comment From: pivotal-cla
@essobedo Please sign the Contributor License Agreement!
Click here to manually synchronize the status of this Pull Request.
See the FAQ for frequently asked questions.
Comment From: sbrannen
Hi @essobedo,
Congratulations on submitting your first PR to the Spring Framework! 👍
- Uses the name of the method instead of the identifier of the
LifecycleElement
even if it won't solve the potential naming conflict but it is a more specific use case
I'm not certain that this is the correct approach to this issue.
I believe we may need to keep the fully-qualified method name in order to support multiple private
init/destroy methods declared at different levels within a class hierarchy.
In light of that, I have marked this PR as a draft to allow us to further investigate the issue.
Related Issues
-
28083
Comment From: essobedo
@sbrannen I understand, I was wondering if I should go beyond or not, but my idea was just to reduce the limitations with very limited impact. Let me try to cover all use cases. Thank you for your quick feedback.
Comment From: snicoll
@essobedo how is it going? Do you need help?
Comment From: sbrannen
FYI: I am already looking into this issue, and I think I will be taking a different approach to addressing this.
Comment From: snicoll
Can we please close it to avoid duplicated efforts?
Comment From: essobedo
Sorry, I had to finish something else first, I planned to work on it tomorrow but if you are already working on it feel free to close it
Comment From: sbrannen
- superseded by #30724