spring-projects / spring-projects/spring-framework

Inconsistent Behavior of BeanPostProcessor Between Singleton and Prototype Scopes

Open
#36,790 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

Description

I noticed that the behavior of a BeanPostProcessor varies depending on its scope, which is quite confusing:

  1. Singleton Scope (Default)
  • The processor works correctly for all other beans.
  • The processor itself does NOT get processed by its own postProcessBeforeInitialization / postProcessAfterInitialization methods.

This happens because the BeanPostProcessor bean is instantiated early during BeanPostProcessor registration, then stored in the singleton pool.

Later, when other non-lazy-init singleton beans are created, since this processor already exists in the singleton pool, its lifecycle is not re-executed, so it does not process itself.

  1. Prototype Scope (@Scope("prototype"))
  • The processor works correctly for all other beans (including beans created after the processor is instantiated).
  • The processor itself DOES get processed by its own postProcessBeforeInitialization / postProcessAfterInitialization methods.

This happens because the prototype-scoped BeanPostProcessor is not cached in the singleton pool. Every time it is created, it goes through the full bean lifecycle, which causes the BeanPostProcessor to apply its own post-processing to itself.

This leads to inconsistent behavior: the same BeanPostProcessor exhibits different lifecycle callback behavior based on its scope.

This inconsistency is not mentioned in the official documentation, which can lead to unexpected and confusing results for developers.

Steps to Reproduce
  1. Define a class UserBeanPostProcessor that implements BeanPostProcessor and logs in both callback methods.
  2. Mark it with @Component (default singleton scope).
  3. Run the application: logs show the processor works for other beans, but no logs for itself.
  4. Add @Scope("prototype") to the processor.
  5. Run the application: logs show the processor works for other beans and processes itself.
Expected Behavior

The behavior should be consistent across both scopes:

  • Either the processor never processes itself (in both singleton and prototype), or
  • The processor always processes itself consistently, with clear documentation about the recursion risks.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reported UserBeanPostProcessor reproduction and compare the singleton and @Scope("prototype") creation paths during BeanPostProcessor registration. Trace the lifecycle callbacks for the processor itself and verify the chosen behavior with coverage for both scopes; done means the behavior is consistent or clearly documented, including recursion risks.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.