Improve Bean Overriding support, testing and documentation

This commit improves on the bean overriding feature in several ways:
the API is simplified and polished (metadata and processor contracts,
 etc...).

The commit also reworks infrastructure classes (context customizer,
test execution listener, BeanOverrideBeanFactoryPostProcessor, etc...).
Parsing of annotations is now fully stateless.

In order to avoid OverrideMetadata in bean definition and to make a
first step towards AOT support, the BeanOverrideBeanFactoryPostProcessor
now delegates to a BeanOverrideRegistrar to track classes to parse,
the metadata-related state as well as for the field injection methods
for tests.

Lastly, this commit increases the test coverage for the provided
annotations and adds integration tests and fixes a few `@TestBean`
issues.
This commit is contained in:
Simon Baslé
2024-03-20 11:48:22 +01:00
parent 711ddd1ac6
commit 2d33aac350
30 changed files with 1714 additions and 820 deletions

View File

@@ -47,6 +47,9 @@ Java::
<2> The result of this static method will be used as the instance and injected into the field
======
NOTE: The method to invoke is searched in the test class and any enclosing class it might
have, as well as its hierarchy. This typically allows nested test class to provide the
method to use in the root test class.
[[spring-testing-annotation-beanoverriding-mockitobean]]
== `@MockitoBean` and `@MockitoSpyBean`
@@ -100,32 +103,31 @@ and associated infrastructure, which allows to define custom bean overriding var
In order to provide an extension, three classes are needed:
- A concrete `BeanOverrideProcessor<P>`.
- A concrete `OverrideMetadata` created by said processor.
- A concrete `BeanOverrideProcessor` implementation, `P`.
- One or more concrete `OverrideMetadata` implementations created by said processor.
- An annotation meta-annotated with `@BeanOverride(P.class)`.
The Spring TestContext Framework includes infrastructure classes that support bean
overriding: a `BeanPostProcessor`, a `TestExecutionListener` and a `ContextCustomizerFactory`.
These are automatically registered via the Spring TestContext Framework `spring.factories`
file.
overriding: a `BeanFactoryPostProcessor`, a `TestExecutionListener` and a `ContextCustomizerFactory`.
The later two are automatically registered via the Spring TestContext Framework
`spring.factories` file, and are responsible for setting up the rest of the infrastructure.
The test classes are parsed looking for any field meta-annotated with `@BeanOverride`,
instantiating the relevant `BeanOverrideProcessor` in order to register an `OverrideMetadata`.
Then the `BeanOverrideBeanPostProcessor` will use that information to alter the Context,
registering and replacing bean definitions as influenced by each metadata
Then the `BeanOverrideBeanFactoryPostProcessor` will use that information to alter the
Context, registering and replacing bean definitions as influenced by each metadata
`BeanOverrideStrategy`:
- `REPLACE_DEFINITION`: the bean post-processor replaces the bean definition.
If it is not present in the context, an exception is thrown.
- `CREATE_OR_REPLACE_DEFINITION`: same as above but if the bean definition is not present
in the context, one is created
- `WRAP_EARLY_BEAN`: an original instance is obtained via
`SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(Object, String)` and
provided to the processor during `OverrideMetadata` creation.
- `WRAP_EARLY_BEAN`: an original instance is obtained and passed to the `OverrideMetadata`
when the override instance is created.
NOTE: The Bean Overriding infrastructure works best with singleton beans. It also doesn't
include any bean resolution (unlike e.g. an `@Autowired`-annotated field). As such, the
name of the bean to override MUST be somehow provided to or computed by the
`BeanOverrideProcessor`. Typically, the end user provides the name as part of the custom
annotation's attributes, or the annotated field's name.
NOTE: The Bean Overriding infrastructure doesn't include any bean resolution step
(unlike e.g. an `@Autowired`-annotated field). As such, the name of the bean to override
MUST be somehow provided to or computed by the `BeanOverrideProcessor`. Typically, the end
user provides the name as part of the custom annotation's attributes, or the annotated
field's name.