Skip to content

Apply InstanceSupplier post-processing when bypassed by explicit args#36462

Open
vlsi wants to merge 2 commits intospring-projects:mainfrom
vlsi:fix/gh-35871-prototype-aot-autowired-setter
Open

Apply InstanceSupplier post-processing when bypassed by explicit args#36462
vlsi wants to merge 2 commits intospring-projects:mainfrom
vlsi:fix/gh-35871-prototype-aot-autowired-setter

Conversation

@vlsi
Copy link
Contributor

@vlsi vlsi commented Mar 13, 2026

Summary

Fixes #35871.

When a bean definition has an InstanceSupplier with andThen() post-processing (as generated by AOT for @Autowired setter injection), calling getBean(name, args) bypasses the InstanceSupplier in createBeanInstance() but the andThen() post-processing was silently skipped. This meant that @Autowired setter injection would not be invoked for prototype beans retrieved with explicit constructor arguments in AOT mode.

This PR introduces InstanceSupplier#postProcessInstance() — a method that applies only the post-processing chain (registered via andThen()) to an already-created instance, without invoking the instance creation itself. AbstractAutowireCapableBeanFactory#doCreateBean() now calls this when it detects that the instance supplier was bypassed due to explicit args.

Changed files

  • InstanceSupplier — new postProcessInstance(RegisteredBean, T) default method; the anonymous class returned by andThen() overrides it to chain through the after function.
  • AbstractAutowireCapableBeanFactory — new hook applyInstanceSupplierPostProcessing() (no-op by default), called in doCreateBean() when args != null and an InstanceSupplier is present.
  • DefaultListableBeanFactory — overrides applyInstanceSupplierPostProcessing() to delegate to InstanceSupplier#postProcessInstance().

Note for reviewers

The post-processing runs after addSingletonFactory() (early singleton caching). For the typical case where postProcessInstance() returns the same instance (e.g. setter injection), this is safe. If a custom andThen() returns a different object (wrapping), the existing circular reference detection at the end of doCreateBean() will detect the identity mismatch and throw BeanCurrentlyInCreationException — consistent with how Spring handles any BeanPostProcessor that changes bean identity. Reviewers may want to evaluate whether moving the post-processing before addSingletonFactory() would be preferable for the wrapping case, though that would diverge from the established pattern for other post-processors.

Test plan

  • PrototypeWithArgsAotTests — end-to-end AOT test: prototype bean with @Autowired setter + BeanFactoryAware, retrieved via getBean(name, args) in a fresh AOT-compiled context
  • DefaultListableBeanFactoryTests#singletonWithInstanceSupplierAndThenPostProcessingAppliedWithExplicitArgs — singleton bean with InstanceSupplier.andThen() + explicit args
  • DefaultListableBeanFactoryTests#prototypeWithInstanceSupplierAndThenPostProcessingAppliedWithExplicitArgs
    — prototype bean with InstanceSupplier.andThen() + explicit args

🤖 Generated with Claude Code (Opus 4.6)
🤖 Cross-reviewed with Codex (GPT 5.4)

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Mar 13, 2026
vlsi and others added 2 commits March 13, 2026 18:13
When getBean(name, args) is called with explicit constructor arguments,
the InstanceSupplier is intentionally bypassed (spring-projectsgh-32657). However, in
AOT mode, @Autowired setter/field injection is baked into the
InstanceSupplier's andThen() chain and AutowiredAnnotationBeanPostProcessor
is excluded from runtime registration. This means the autowiring
post-processing is lost when the supplier is bypassed.

Add InstanceSupplier.postProcessInstance() to allow applying only the
post-processing steps (from andThen()) to an already-created instance
without re-invoking creation. Call this from doCreateBean() when the
instance supplier was bypassed due to explicit args.

Closes spring-projectsgh-35871

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Vladimir Sitnikov <sitnikov.vladimir@gmail.com>
…xplicit args

Clarify that applyInstanceSupplierPostProcessing() can also run for
singleton beans created via getBean(name, args), not only prototypes.
The previous comment incorrectly stated this code path only applies to
non-singleton beans. In practice, getBean(name, args) can create a
singleton on first lookup since doGetBean only reuses the singleton
cache when args == null.

The ordering (post-processing after addSingletonFactory) is consistent
with how Spring handles any BeanPostProcessor that changes bean identity:
circular reference detection at the end of doCreateBean will detect the
mismatch and throw BeanCurrentlyInCreationException. For the typical
case where post-processing returns the same instance (e.g. setter
injection), this is safe.

Add two tests verifying InstanceSupplier.andThen() post-processing is
applied when the instance supplier is bypassed due to explicit args,
for both singleton and prototype scopes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Vladimir Sitnikov <sitnikov.vladimir@gmail.com>
@vlsi vlsi force-pushed the fix/gh-35871-prototype-aot-autowired-setter branch from 8d1244f to 294d050 Compare March 13, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spring AOT processed application does not properly initialize Prototype Bean

2 participants