Skip to content

Inconsistent Behavior of BeanPostProcessor Between Singleton and Prototype Scopes #36790

@15648933053

Description

@15648933053

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions