Support Bean Overrides for non-singletons

Prior to this commit, the BeanOverrideBeanFactoryPostProcessor rejected
any attempt to override a non-singleton bean; however, due to interest
from the community, we have decided to provide support for overriding
non-singleton beans via the Bean Override mechanism — for example, when
using @⁠MockitoBean, @⁠MockitoSpyBean, and @⁠TestBean.

With this commit, we now support Bean Overrides for non-singletons: for
standard JVM runtimes as well as AOT processing and AOT runtimes. This
commit also documents that non-singletons will effectively be converted
to singletons when overridden and logs a warning similar to the
following.

WARN: BeanOverrideBeanFactoryPostProcessor - Converting 'prototype' scoped bean definition 'myBean' to a singleton.

See gh-33602
See gh-32933
See gh-33800
Closes gh-35574
This commit is contained in:
Sam Brannen
2025-10-06 15:25:47 +02:00
parent ff9a349271
commit 30db2e4fb5
15 changed files with 250 additions and 53 deletions
@@ -88,14 +88,20 @@ To avoid such undesired side effects, consider using
[NOTE]
====
Only _singleton_ beans can be overridden. Any attempt to override a non-singleton bean
will result in an exception.
When using `@MockitoBean` to mock a non-singleton bean, the non-singleton bean will be
replaced with a singleton mock, and the corresponding bean definition will be converted
to a `singleton`. Consequently, if you mock a `prototype` or scoped bean, the mock will
be treated as a `singleton`.
Similarly, when using `@MockitoSpyBean` to create a spy for a non-singleton bean, the
corresponding bean definition will be converted to a `singleton`. Consequently, if you
create a spy for a `prototype` or scoped bean, the spy will be treated as a `singleton`.
When using `@MockitoBean` to mock a bean created by a `FactoryBean`, the `FactoryBean`
will be replaced with a singleton mock of the type of object created by the `FactoryBean`.
When using `@MockitoSpyBean` to create a spy for a `FactoryBean`, a spy will be created
for the object created by the `FactoryBean`, not for the `FactoryBean` itself.
Similarly, when using `@MockitoSpyBean` to create a spy for a `FactoryBean`, a spy will
be created for the object created by the `FactoryBean`, not for the `FactoryBean` itself.
====
[NOTE]
@@ -116,12 +116,15 @@ fully-qualified method name following the syntax `<fully-qualified class name>#<
for example, `methodName = "org.example.TestUtils#createCustomService"`.
====
[TIP]
[NOTE]
====
Only _singleton_ beans can be overridden. Any attempt to override a non-singleton bean
will result in an exception.
When overriding a non-singleton bean, the non-singleton bean will be replaced with a
singleton bean corresponding to the value returned from the `@TestBean` factory method,
and the corresponding bean definition will be converted to a `singleton`. Consequently,
if `@TestBean` is used to override a `prototype` or scoped bean, the overridden bean will
be treated as a `singleton`.
When overriding a bean created by a `FactoryBean`, the `FactoryBean` will be replaced
with a singleton bean corresponding to the value returned from the `@TestBean` factory
method.
Similarly, when overriding a bean created by a `FactoryBean`, the `FactoryBean` will be
replaced with a singleton bean corresponding to the value returned from the `@TestBean`
factory method.
====
@@ -62,8 +62,11 @@ defined by the corresponding `BeanOverrideStrategy`:
[TIP]
====
Only _singleton_ beans can be overridden. Any attempt to override a non-singleton bean
will result in an exception.
When replacing a non-singleton bean, the non-singleton bean will be replaced with a
singleton bean corresponding to bean override instance created by the applicable
`BeanOverrideHandler`, and the corresponding bean definition will be converted to a
`singleton`. Consequently, if a handler overrides a `prototype` or scoped bean, the
overridden bean will be treated as a `singleton`.
When replacing a bean created by a `FactoryBean`, the `FactoryBean` itself will be
replaced with a singleton bean corresponding to bean override instance created by the