Skip to content

Commit

Permalink
Polish reference documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Jun 11, 2024
1 parent 60b5bbe commit f481a4b
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,19 @@ therefore recommendable for your parameter names to match the target bean names.
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
which is semantically defined to identify a specific target component by its unique name,
with the declared type being irrelevant for the matching process. `@Autowired` has rather
different semantics: After selecting candidate beans by type, the specified `String`
different semantics: after selecting candidate beans by type, the specified `String`
qualifier value is considered within those type-selected candidates only (for example,
matching an `account` qualifier against beans marked with the same qualifier label).

For beans that are themselves defined as a collection, `Map`, or array type, `@Resource`
is a fine solution, referring to the specific collection or array bean by unique name.
That said, as of 4.3, you can match collection, `Map`, and array types through Spring's
That said, you can match collection, `Map`, and array types through Spring's
`@Autowired` type matching algorithm as well, as long as the element type information
is preserved in `@Bean` return type signatures or collection inheritance hierarchies.
In this case, you can use qualifier values to select among same-typed collections,
as outlined in the previous paragraph.

As of 4.3, `@Autowired` also considers self references for injection (that is, references
`@Autowired` also considers self references for injection (that is, references
back to the bean that is currently injected). Note that self injection is a fallback.
Regular dependencies on other components always have precedence. In that sense, self
references do not participate in regular candidate selection and are therefore in
Expand Down
21 changes: 11 additions & 10 deletions framework-docs/modules/ROOT/pages/core/beans/definition.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,29 @@ lead to concurrent access exceptions, inconsistent state in the bean container,
[[beans-definition-overriding]]
== Overriding Beans

Bean overriding is happening when a bean is registered using an identifier that is
already allocated. While bean overriding is possible, it makes the configuration harder
to read and this feature will be deprecated in a future release.
Bean overriding occurs when a bean is registered using an identifier that is already
allocated. While bean overriding is possible, it makes the configuration harder to read.

WARNING: Bean overriding will be deprecated in a future release.

To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding`
flag to `false` on the `ApplicationContext` before it is refreshed. In such setup, an
flag to `false` on the `ApplicationContext` before it is refreshed. In such a setup, an
exception is thrown if bean overriding is used.

By default, the container logs every bean overriding at `INFO` level so that you can
adapt your configuration accordingly. While not recommended, you can silence those logs
by setting the `allowBeanDefinitionOverriding` flag to `true`.
By default, the container logs every attempt to override a bean at `INFO` level so that
you can adapt your configuration accordingly. While not recommended, you can silence
those logs by setting the `allowBeanDefinitionOverriding` flag to `true`.

.Java-configuration
.Java Configuration
****
If you use Java Configuration, a corresponding `@Bean` method always silently overrides
a scanned bean class with the same component name as long as the return type of the
`@Bean` method matches that bean class. This simply means that the container will call
the `@Bean` factory method in favor of any pre-declared constructor on the bean class.
****

NOTE: We acknowledge that overriding beans in a test scenario is convenient,
and there is explicit support for this as of Spring Framework 6.2. Please refer to
NOTE: We acknowledge that overriding beans in test scenarios is convenient, and there is
explicit support for this as of Spring Framework 6.2. Please refer to
xref:testing/testcontext-framework/bean-overriding.adoc[this section] for more details.


Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,37 @@
[[spring-testing-annotation-beanoverriding-mockitobean]]
= `@MockitoBean` and `@MockitoSpyBean`

`@MockitoBean` and `@MockitoSpyBean` are used on test class fields to override beans in
the test's `ApplicationContext` with a Mockito mock or spy, respectively. In the latter
case, the original bean definition is not replaced, but instead an early instance of the
bean is captured and wrapped by the spy.
`@MockitoBean` and `@MockitoSpyBean` are used on fields in test classes to override beans
in the test's `ApplicationContext` with a Mockito mock or spy, respectively. In the
latter case, the original bean definition is not replaced, but instead an early instance
of the bean is captured and wrapped by the spy.

By default, the annotated field's type is used to search for candidate definitions to
override. If multiple candidates match, the usual `@Qualifier` can be provided to
narrow the candidate to override. Alternatively, a candidate whose bean definition name
matches the name of the field will match.
By default, the annotated field's type is used to search for candidate bean definitions
to override. If multiple candidates match, `@Qualifier` can be provided to narrow the
candidate to override. Alternatively, a candidate whose bean definition name matches the
name of the field will match.

To use a by-name override rather than a by-type override, specify the `name` attribute
of the annotation.

[WARNING]
====
The qualifiers, including the name of the field are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to mock or
spy the same bean in several tests, make sure to name the field consistently to avoid
Qualifiers, including the name of the field, are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to mock or spy
the same bean in several tests, make sure to name the field consistently to avoid
creating unnecessary contexts.
====

Each annotation also defines Mockito-specific attributes to fine-tune the mocking details.

The `@MockitoBean` annotation uses the `REPLACE_OR_CREATE_DEFINITION`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy for test bean overriding].

If no definition matches, then a definition is created on-the-fly.
If no existing bean definition matches, a new bean definition is created on the fly.

The `@MockitoSpyBean` annotation uses the `WRAP_BEAN`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy],
and the original instance is wrapped in a Mockito spy.

It requires that exactly one candidate definition exists.
and the original instance is wrapped in a Mockito spy. This strategy requires that
exactly one candidate bean definition exists.

The following example shows how to use the default behavior of the `@MockitoBean` annotation:

Expand All @@ -44,20 +42,20 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoBean // <1>
private CustomService customService;
@MockitoBean // <1>
CustomService customService;
// test case body...
}
----
<1> Replace the bean with type `CustomService` with a Mockito `mock`.
======

In the example above, we are creating a mock for `CustomService`. If more that
one bean with such type exist, the bean named `customService` is considered. Otherwise,
the test will fail and you will need to provide a qualifier of some sort to identify which
of the `CustomService` beans you want to override. If no such bean exists, a bean
definition will be created with an auto-generated bean name.
In the example above, we are creating a mock for `CustomService`. If more than one bean
of that type exists, the bean named `customService` is considered. Otherwise, the test
will fail, and you will need to provide a qualifier of some sort to identify which of the
`CustomService` beans you want to override. If no such bean exists, a bean definition
will be created with an auto-generated bean name.

The following example uses a by-name lookup, rather than a by-type lookup:

Expand All @@ -68,14 +66,14 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoBean(name = "service") // <1>
private CustomService customService;
@MockitoBean(name = "service") // <1>
CustomService customService;
// test case body...
}
----
<1> Replace the bean named `service` with a Mockito `mock`.
<1> Replace the bean named `service` with a Mockito `mock`.
======

If no bean definition named `service` exists, one is created.
Expand All @@ -89,19 +87,19 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoSpyBean // <1>
private CustomService customService;
@MockitoSpyBean // <1>
CustomService customService;
// test case body...
}
----
<1> Wrap the bean with type `CustomService` with a Mockito `spy`.
======

In the example above, we are wrapping the bean with type `CustomService`. If more that
one bean with such type exist, the bean named `customService` is considered. Otherwise,
the test will fail and you will need to provide a qualifier of some sort to identify which
of the `CustomService` beans you want to spy.
In the example above, we are wrapping the bean with type `CustomService`. If more than
one bean of that type exists, the bean named `customService` is considered. Otherwise,
the test will fail, and you will need to provide a qualifier of some sort to identify
which of the `CustomService` beans you want to spy.

The following example uses a by-name lookup, rather than a by-type lookup:

Expand All @@ -112,12 +110,12 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@MockitoSpyBean(name = "service") // <1>
private CustomService customService;
@MockitoSpyBean(name = "service") // <1>
CustomService customService;
// test case body...
}
----
<1> Wrap the bean named `service` with a Mockito `spy`.
<1> Wrap the bean named `service` with a Mockito `spy`.
======
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
[[spring-testing-annotation-beanoverriding-testbean]]
= `@TestBean`

`@TestBean` is used on a test class field to override a specific bean in the test's
`ApplicationContext` with an instance provided by a conventionally named static factory
method.
`@TestBean` is used on a field in a test class to override a specific bean in the test's
`ApplicationContext` with an instance provided by a factory method.

The associated factory method name is derived from the annotated field's name, or bean
name if specified. A `static` method with no argument that returns a type compatible
with the type of the bean to override is expected. To make things more explicit, or if
you'd rather use a different name, the annotation allows for a specific method name to
be provided.
The associated factory method name is derived from the annotated field's name, or the
bean name if specified. The factory method must be `static`, accept no arguments, and
have a return type compatible with the type of the bean to override. To make things more
explicit, or if you'd rather use a different name, the annotation allows for a specific
method name to be provided.

By default, the annotated field's type is used to search for candidate definitions to
override. If multiple candidates match, the usual `@Qualifier` can be provided to
narrow the candidate to override. Alternatively, a candidate whose bean definition name
matches the name of the field will match.
By default, the annotated field's type is used to search for candidate bean definitions
to override. If multiple candidates match, `@Qualifier` can be provided to narrow the
candidate to override. Alternatively, a candidate whose bean definition name matches the
name of the field will match.

To use a by-name override rather than a by-type override, specify the `name` attribute
of the annotation.

[WARNING]
====
The qualifiers, including the name of the field are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to override
the same bean in several tests, make sure to name the field consistently to avoid
creating unnecessary contexts.
Qualifiers, including the name of the field, are used to determine if a separate
`ApplicationContext` needs to be created. If you are using this feature to override the
same bean in several tests, make sure to name the field consistently to avoid creating
unnecessary contexts.
====

The following example shows how to use the default behavior of the `@TestBean` annotation:
Expand All @@ -36,25 +35,24 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@TestBean // <1>
private CustomService customService;
@TestBean // <1>
CustomService customService;
// test case body...
private static CustomService customService() { // <2>
static CustomService customService() { // <2>
return new MyFakeCustomService();
}
}
----
<1> Mark a field for overriding of the bean with type `CustomService`.
<1> Mark a field for overriding the bean with type `CustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
======

In the example above, we are overriding the bean with type `CustomService`. If more that
one bean with such type exist, the bean named `customService` is considered. Otherwise,
the test will fail and you will need to provide a qualifier of some sort to identify which
of the `CustomService` beans you want to override.

In the example above, we are overriding the bean with type `CustomService`. If more than
one bean of that type exists, the bean named `customService` is considered. Otherwise,
the test will fail, and you will need to provide a qualifier of some sort to identify
which of the `CustomService` beans you want to override.

The following example uses a by-name lookup, rather than a by-type lookup:

Expand All @@ -65,17 +63,18 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class OverrideBeanTests {
@TestBean(name = "service", methodName = "createCustomService") // <1>
private CustomService customService;
@TestBean(name = "service", methodName = "createCustomService") // <1>
CustomService customService;
// test case body...
private static CustomService createCustomService() { // <2>
static CustomService createCustomService() { // <2>
return new MyFakeCustomService();
}
}
----
<1> Mark a field for overriding of the bean with name `service`.
<1> Mark a field for overriding the bean with name `service`, and specify that the
factory method is named `createCustomService`.
<2> The result of this static method will be used as the instance and injected into the field.
======

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@ protected OverrideMetadata(Field field, ResolvableType beanType, @Nullable Strin
}

/**
* Parse the given {@code testClass} and provide the use of bean override.
* Parse the given {@code testClass} and build the corresponding list of
* bean {@code OverrideMetadata}.
* @param testClass the class to parse
* @return a list of bean overrides metadata
* @return a list of {@code OverrideMetadata}
*/
public static List<OverrideMetadata> forTestClass(Class<?> testClass) {
List<OverrideMetadata> all = new LinkedList<>();
ReflectionUtils.doWithFields(testClass, field -> parseField(field, testClass, all));
return all;
List<OverrideMetadata> metadataList = new LinkedList<>();
ReflectionUtils.doWithFields(testClass, field -> parseField(field, testClass, metadataList));
return metadataList;
}

private static void parseField(Field field, Class<?> testClass, List<OverrideMetadata> metadataList) {
Expand Down Expand Up @@ -182,9 +183,9 @@ public boolean equals(Object other) {

@Override
public int hashCode() {
int hash = Objects.hash(getClass().hashCode(), this.beanType.getType(), this.beanName, this.strategy);
int hash = Objects.hash(getClass(), this.beanType.getType(), this.beanName, this.strategy);
return (this.beanName != null ? hash : hash +
Objects.hash(this.field.getName(), Arrays.hashCode(this.field.getAnnotations())));
31 * Objects.hash(this.field.getName(), Arrays.hashCode(this.field.getAnnotations())));
}

@Override
Expand Down

0 comments on commit f481a4b

Please sign in to comment.