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

beanFactory#getBean with arguments ignore them if an Instance supplier is defined #32657

Closed
antonioiorfino opened this issue Apr 17, 2024 · 3 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@antonioiorfino
Copy link

When use a BeanFactory inside my project I have two different result for the same operation after standard build and native build.
The different behavior is related to prototype's scope bean when use the following methods:

  • beanFactory.getBean(Class requiredType, Object... args)
  • beanFactory.autowireBean(Object existingBean)
  • beanFactory.initializeBean(Object existingBean, String beanName)

I have created a simple project in order to explain better my problem spring-boot-test.
It is based on spring-boot 3.2.4 with GraalVM

Java version: 17.0.11, vendor: Oracle Corporation

I didn't seen something on documentation, I hope it is my mistake.

Command Pattern

When call the api http://localhost:8080/test/1 the expectation is to set the value "1" using the CommandExample constructor. This approach it works on standard build, with native the beanFactory will create a prototype using always the empty constructor. The result is the command not have any data to proceed with the elaboration.

Initialization Bean

When call the api http://localhost:8080/instance the expectation is to instantiate a BeanInstanceExample class programmatically during the initialization of the ServiceBeanInstanceExample.
Also in this case the initialization of the BeanInstanceExample with the autowireBean and initializeBean works fine with the standard build, but not work in a native build.

I hope everything is clear

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 17, 2024
@bclozel bclozel transferred this issue from spring-projects/spring-boot Apr 17, 2024
@jhoeller jhoeller added in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing labels Apr 17, 2024
@snicoll snicoll changed the title Different behavior when use BeanFactory in native build instead of standard build beanFactory#getBean with arguments calls the default constructor on Native Apr 22, 2024
@snicoll snicoll added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 22, 2024
@snicoll snicoll added this to the 6.1.x milestone Apr 22, 2024
@snicoll
Copy link
Member

snicoll commented Apr 22, 2024

Thanks for the report. I can see that the BeanFactory indeed calls the default constructor of CommandExample albeit being provided with the integer argument.

The resolution of the constructor is highly dynamic and AOT cannot detect this pattern at build time. I had hoped that providing the necessary reflection hints would fix it but it does not work and will need some further investigation.

@snicoll snicoll changed the title beanFactory#getBean with arguments calls the default constructor on Native beanFactory#getBean with arguments ignore them if an Instance supplier is defined Apr 22, 2024
@snicoll
Copy link
Member

snicoll commented Apr 22, 2024

So it turns out that the code path of this method does not ignore an instance supplier if one is defined. This explains why the default constructor is invoked albeit a matching argument being provided.

Please note that creating a bean like this is not idiomatic Spring and we won't be able to infer the necessary hints as we can't detect at build time that this constructor is going to be invoked by reflection.

@snicoll snicoll removed the theme: aot An issue related to Ahead-of-time processing label Apr 22, 2024
@snicoll snicoll self-assigned this Apr 22, 2024
@snicoll snicoll modified the milestones: 6.1.x, 6.1.7 Apr 22, 2024
@snicoll
Copy link
Member

snicoll commented Apr 22, 2024

I didn't seen something on documentation, I hope it is my mistake.

Yes and no. There was definitely a bug that we used an instance supplier even in the case getBean was used with custom arguments. So I fixed that but the whole thing is a bit strange with AOT/native.

The instance supplier is used to apply the optimizations we've discovered at build-time. Typically, the autowiring on the field in CommandExample is processed there, rather than discovering things at runtime. The pattern you've used means that you'd like to instantiate the bean in a custom manner. This has two consequences:

  1. We don't know the constructor you're going to invoke as the arguments are in your own code with no way to discover them. As such, the sample with this fix would not work until you add some reflection hint for the constructor that takes the Integer argument.
  2. Now that the instance supplier is discarded, the autowiring is no longer applied and that field is null.

Looking at your simplified example, I wonder if CommandExample has to be a bean. IMO it would be better to provide the service and the integer to the constructor, and resolve the service in your controller. I am sure your actual code is more involved but not making it the command a bean is something you should be pursuing for native compatibility. In particular, getBean with arguments is not recommended. I've created #32690 to document that bit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants