From 6f5d3a4b12719dfced66582f46b6887e7763b796 Mon Sep 17 00:00:00 2001
From: Sam Brannen <104798+sbrannen@users.noreply.github.com>
Date: Sun, 10 Mar 2024 16:05:55 +0100
Subject: [PATCH] Polish Bean Override support in the TestContext framework
---
.../test/bean/override/BeanOverride.java | 21 +--
.../BeanOverrideBeanPostProcessor.java | 128 ++++++++++--------
.../BeanOverrideContextCustomizerFactory.java | 11 +-
.../bean/override/BeanOverrideParser.java | 61 +++++----
.../bean/override/BeanOverrideProcessor.java | 38 +++---
.../bean/override/BeanOverrideStrategy.java | 18 +--
.../BeanOverrideTestExecutionListener.java | 46 ++++---
.../test/bean/override/OverrideMetadata.java | 42 +++---
.../bean/override/mockito/Definition.java | 9 +-
.../bean/override/mockito/MockDefinition.java | 29 ++--
.../test/bean/override/mockito/MockReset.java | 3 +-
.../bean/override/mockito/MockitoBean.java | 29 ++--
.../mockito/MockitoBeanOverrideProcessor.java | 3 +-
.../bean/override/mockito/MockitoBeans.java | 4 +-
.../MockitoResetTestExecutionListener.java | 3 +-
.../bean/override/mockito/MockitoSpyBean.java | 19 +--
.../mockito/MockitoTestExecutionListener.java | 27 ++--
.../bean/override/mockito/SpyDefinition.java | 40 +++---
.../BeanOverrideBeanPostProcessorTests.java | 29 ++--
.../override/BeanOverrideParserTests.java | 58 ++++----
.../example/ExampleBeanOverrideProcessor.java | 5 +-
21 files changed, 332 insertions(+), 291 deletions(-)
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverride.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverride.java
index 114f85769500..9872885ec5ed 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverride.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverride.java
@@ -23,24 +23,27 @@
/**
* Mark an annotation as eligible for Bean Override parsing.
- * This meta-annotation provides a {@link BeanOverrideProcessor} class which
- * must be capable of handling the annotated annotation.
*
- *
Target annotation must have a {@link RetentionPolicy} of {@code RUNTIME}
- * and be applicable to {@link java.lang.reflect.Field Fields} only.
- * @see BeanOverrideBeanPostProcessor
+ *
This meta-annotation specifies a {@link BeanOverrideProcessor} class which
+ * must be capable of handling the composed annotation that is meta-annotated
+ * with {@code @BeanOverride}.
+ *
+ *
The composed annotation that is meta-annotated with {@code @BeanOverride}
+ * must have a {@code RetentionPolicy} of {@link RetentionPolicy#RUNTIME RUNTIME}
+ * and a {@code Target} of {@link ElementType#FIELD FIELD}.
*
* @author Simon Baslé
* @since 6.2
+ * @see BeanOverrideBeanPostProcessor
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.ANNOTATION_TYPE})
+@Target(ElementType.ANNOTATION_TYPE)
public @interface BeanOverride {
/**
- * A {@link BeanOverrideProcessor} implementation class by which the target
- * annotation should be processed. Implementations must have a no-argument
- * constructor.
+ * A {@link BeanOverrideProcessor} implementation class by which the composed
+ * annotation should be processed.
*/
Class extends BeanOverrideProcessor> value();
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideBeanPostProcessor.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideBeanPostProcessor.java
index e6561e1bba3c..d6433788bc82 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideBeanPostProcessor.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideBeanPostProcessor.java
@@ -53,19 +53,20 @@
/**
* A {@link BeanFactoryPostProcessor} used to register and inject overriding
- * bean metadata with the {@link ApplicationContext}. A set of
- * {@link OverrideMetadata} must be passed to the processor.
- * A {@link BeanOverrideParser} can typically be used to parse these from test
- * classes that use any annotation meta-annotated with {@link BeanOverride} to
- * mark override sites.
+ * bean metadata with the {@link ApplicationContext}.
*
- *
This processor supports two {@link BeanOverrideStrategy}:
+ *
A set of {@link OverrideMetadata} must be provided to this processor. A
+ * {@link BeanOverrideParser} can typically be used to parse this metadata from
+ * test classes that use any annotation meta-annotated with
+ * {@link BeanOverride @BeanOverride} to mark override sites.
+ *
+ *
This processor supports two types of {@link BeanOverrideStrategy}:
*
- *
replacing a given bean's definition, immediately preparing a singleton
+ *
Replacing a given bean's definition, immediately preparing a singleton
* instance
- *
intercepting the actual bean instance upon creation and wrapping it,
+ *
Intercepting the actual bean instance upon creation and wrapping it,
* using the early bean definition mechanism of
- * {@link SmartInstantiationAwareBeanPostProcessor}).
This processor also provides support for injecting the overridden bean
@@ -78,19 +79,25 @@ public class BeanOverrideBeanPostProcessor implements InstantiationAwareBeanPost
BeanFactoryAware, BeanFactoryPostProcessor, Ordered {
private static final String INFRASTRUCTURE_BEAN_NAME = BeanOverrideBeanPostProcessor.class.getName();
- private static final String EARLY_INFRASTRUCTURE_BEAN_NAME = BeanOverrideBeanPostProcessor.WrapEarlyBeanPostProcessor.class.getName();
- private final Set overrideMetadata;
- private final Map earlyOverrideMetadata = new HashMap<>();
+ private static final String EARLY_INFRASTRUCTURE_BEAN_NAME =
+ BeanOverrideBeanPostProcessor.WrapEarlyBeanPostProcessor.class.getName();
- private ConfigurableListableBeanFactory beanFactory;
+
+ private final Map earlyOverrideMetadata = new HashMap<>();
private final Map beanNameRegistry = new HashMap<>();
private final Map fieldRegistry = new HashMap<>();
+ private final Set overrideMetadata;
+
+ @Nullable
+ private ConfigurableListableBeanFactory beanFactory;
+
+
/**
- * Create a new {@link BeanOverrideBeanPostProcessor} instance with the
+ * Create a new {@code BeanOverrideBeanPostProcessor} instance with the
* given {@link OverrideMetadata} set.
* @param overrideMetadata the initial override metadata
*/
@@ -107,7 +114,7 @@ public int getOrder() {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
- "Beans overriding can only be used with a ConfigurableListableBeanFactory");
+ "Bean overriding can only be used with a ConfigurableListableBeanFactory");
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
@@ -120,7 +127,7 @@ protected Set getOverrideMetadata() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- Assert.state(this.beanFactory == beanFactory, "Unexpected beanFactory to postProcess");
+ Assert.state(this.beanFactory == beanFactory, "Unexpected BeanFactory to post-process");
Assert.isInstanceOf(BeanDefinitionRegistry.class, beanFactory,
"Bean overriding annotations can only be used on bean factories that implement "
+ "BeanDefinitionRegistry");
@@ -128,17 +135,17 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
}
private void postProcessWithRegistry(BeanDefinitionRegistry registry) {
- //Note that a tracker bean is registered down the line only if there is some overrideMetadata parsed
- Set overrideMetadata = getOverrideMetadata();
- for (OverrideMetadata metadata : overrideMetadata) {
+ // Note that a tracker bean is registered down the line only if there is some overrideMetadata parsed.
+ for (OverrideMetadata metadata : getOverrideMetadata()) {
registerBeanOverride(registry, metadata);
}
}
/**
- * Copy the details of a {@link BeanDefinition} to the definition created by
- * this processor for a given {@link OverrideMetadata}. Defaults to copying
- * the {@link BeanDefinition#isPrimary()} attribute and scope.
+ * Copy certain details of a {@link BeanDefinition} to the definition created by
+ * this processor for a given {@link OverrideMetadata}.
+ *
The default implementation copies the {@linkplain BeanDefinition#isPrimary()
+ * primary flag} and the {@linkplain BeanDefinition#getScope() scope}.
*/
protected void copyBeanDefinitionDetails(BeanDefinition from, RootBeanDefinition to) {
to.setPrimary(from.isPrimary());
@@ -155,6 +162,7 @@ private void registerBeanOverride(BeanDefinitionRegistry registry, OverrideMetad
private void registerReplaceDefinition(BeanDefinitionRegistry registry, OverrideMetadata overrideMetadata,
boolean enforceExistingDefinition) {
+
RootBeanDefinition beanDefinition = createBeanDefinition(overrideMetadata);
String beanName = overrideMetadata.getExpectedBeanName();
@@ -166,7 +174,7 @@ private void registerReplaceDefinition(BeanDefinitionRegistry registry, Override
}
else if (enforceExistingDefinition) {
throw new IllegalStateException("Unable to override " + overrideMetadata.getBeanOverrideDescription() +
- " bean, expected a bean definition to replace with name '" + beanName + "'");
+ " bean; expected a bean definition to replace with name '" + beanName + "'");
}
registry.registerBeanDefinition(beanName, beanDefinition);
@@ -185,10 +193,10 @@ else if (enforceExistingDefinition) {
/**
* Check that the expected bean name is registered and matches the type to override.
- * If so, put the override metadata in the early tracking map.
- * The map will later be checked to see if a given bean should be wrapped
+ *
If so, put the override metadata in the early tracking map.
+ *
The map will later be checked to see if a given bean should be wrapped
* upon creation, during the {@link WrapEarlyBeanPostProcessor#getEarlyBeanReference(Object, String)}
- * phase
+ * phase.
*/
private void registerWrapEarly(OverrideMetadata metadata) {
Set existingBeanNames = getExistingBeanNames(metadata.typeToOverride());
@@ -203,11 +211,12 @@ private void registerWrapEarly(OverrideMetadata metadata) {
}
/**
- * Check early overrides records and use the {@link OverrideMetadata} to
+ * Check early override records and use the {@link OverrideMetadata} to
* create an override instance from the provided bean, if relevant.
*
Called during the {@link SmartInstantiationAwareBeanPostProcessor}
- * phases (see {@link WrapEarlyBeanPostProcessor#getEarlyBeanReference(Object, String)}
- * and {@link WrapEarlyBeanPostProcessor#postProcessAfterInitialization(Object, String)}).
+ * phases.
+ * @see WrapEarlyBeanPostProcessor#getEarlyBeanReference(Object, String)
+ * @see WrapEarlyBeanPostProcessor#postProcessAfterInitialization(Object, String)
*/
protected final Object wrapIfNecessary(Object bean, String beanName) throws BeansException {
final OverrideMetadata metadata = this.earlyOverrideMetadata.get(beanName);
@@ -236,17 +245,15 @@ private Set getExistingBeanNames(ResolvableType resolvableType) {
beans.add(beanName);
}
}
- beans.removeIf(this::isScopedTarget);
+ beans.removeIf(ScopedProxyUtils::isScopedTarget);
return beans;
}
- private boolean isScopedTarget(String beanName) {
- try {
- return ScopedProxyUtils.isScopedTarget(beanName);
- }
- catch (Throwable ex) {
- return false;
- }
+ @Override
+ public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
+ throws BeansException {
+ ReflectionUtils.doWithFields(bean.getClass(), field -> postProcessField(bean, field));
+ return pvs;
}
private void postProcessField(Object bean, Field field) {
@@ -256,16 +263,10 @@ private void postProcessField(Object bean, Field field) {
}
}
- @Override
- public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
- throws BeansException {
- ReflectionUtils.doWithFields(bean.getClass(), field -> postProcessField(bean, field));
- return pvs;
- }
-
void inject(Field field, Object target, OverrideMetadata overrideMetadata) {
String beanName = this.beanNameRegistry.get(overrideMetadata);
- Assert.state(StringUtils.hasLength(beanName), () -> "No bean found for overrideMetadata " + overrideMetadata);
+ Assert.state(StringUtils.hasLength(beanName),
+ () -> "No bean found for OverrideMetadata: " + overrideMetadata);
inject(field, target, beanName);
}
@@ -287,25 +288,26 @@ private void inject(Field field, Object target, String beanName) {
}
/**
- * Register the processor with a {@link BeanDefinitionRegistry}.
- * Not required when using the Spring TestContext Framework, as registration
- * is automatic via the {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
+ * Register a {@link BeanOverrideBeanPostProcessor} with a {@link BeanDefinitionRegistry}.
+ *
Not required when using the Spring TestContext Framework, as registration
+ * is automatic via the
+ * {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
* mechanism.
* @param registry the bean definition registry
* @param overrideMetadata the initial override metadata set
*/
public static void register(BeanDefinitionRegistry registry, @Nullable Set overrideMetadata) {
- //early processor
- getOrAddInfrastructureBeanDefinition(registry, WrapEarlyBeanPostProcessor.class, EARLY_INFRASTRUCTURE_BEAN_NAME,
- constructorArguments -> constructorArguments.addIndexedArgumentValue(0,
- new RuntimeBeanReference(INFRASTRUCTURE_BEAN_NAME)));
-
- //main processor
- BeanDefinition definition = getOrAddInfrastructureBeanDefinition(registry, BeanOverrideBeanPostProcessor.class,
- INFRASTRUCTURE_BEAN_NAME, constructorArguments -> constructorArguments
- .addIndexedArgumentValue(0, new LinkedHashSet()));
- ConstructorArgumentValues.ValueHolder constructorArg = definition.getConstructorArgumentValues()
- .getIndexedArgumentValue(0, Set.class);
+ // Early processor
+ getOrAddInfrastructureBeanDefinition(
+ registry, WrapEarlyBeanPostProcessor.class, EARLY_INFRASTRUCTURE_BEAN_NAME, constructorArgs ->
+ constructorArgs.addIndexedArgumentValue(0, new RuntimeBeanReference(INFRASTRUCTURE_BEAN_NAME)));
+
+ // Main processor
+ BeanDefinition definition = getOrAddInfrastructureBeanDefinition(
+ registry, BeanOverrideBeanPostProcessor.class, INFRASTRUCTURE_BEAN_NAME, constructorArgs ->
+ constructorArgs.addIndexedArgumentValue(0, new LinkedHashSet()));
+ ConstructorArgumentValues.ValueHolder constructorArg =
+ definition.getConstructorArgumentValues().getIndexedArgumentValue(0, Set.class);
@SuppressWarnings("unchecked")
Set existing = (Set) constructorArg.getValue();
if (overrideMetadata != null && existing != null) {
@@ -315,6 +317,7 @@ public static void register(BeanDefinitionRegistry registry, @Nullable Set clazz, String beanName, Consumer constructorArgumentsConsumer) {
+
if (!registry.containsBeanDefinition(beanName)) {
RootBeanDefinition definition = new RootBeanDefinition(clazz);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
@@ -326,17 +329,21 @@ private static BeanDefinition getOrAddInfrastructureBeanDefinition(BeanDefinitio
return registry.getBeanDefinition(beanName);
}
+
private static final class WrapEarlyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
PriorityOrdered {
private final BeanOverrideBeanPostProcessor mainProcessor;
+
private final Map earlyReferences;
+
private WrapEarlyBeanPostProcessor(BeanOverrideBeanPostProcessor mainProcessor) {
this.mainProcessor = mainProcessor;
this.earlyReferences = new ConcurrentHashMap<>(16);
}
+
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
@@ -363,8 +370,9 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
}
private String getCacheKey(Object bean, String beanName) {
- return StringUtils.hasLength(beanName) ? beanName : bean.getClass().getName();
+ return (StringUtils.hasLength(beanName) ? beanName : bean.getClass().getName());
}
}
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideContextCustomizerFactory.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideContextCustomizerFactory.java
index cf394301d75a..d7a25db125df 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideContextCustomizerFactory.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideContextCustomizerFactory.java
@@ -19,7 +19,6 @@
import java.util.List;
import java.util.Set;
-import org.springframework.aot.hint.annotation.Reflective;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
@@ -29,7 +28,8 @@
import org.springframework.test.context.TestContextAnnotationUtils;
/**
- * A {@link ContextCustomizerFactory} to add support for Bean Overriding.
+ * {@link ContextCustomizerFactory} which provides support for Bean Overriding
+ * in tests.
*
* @author Simon Baslé
* @since 6.2
@@ -39,6 +39,7 @@ public class BeanOverrideContextCustomizerFactory implements ContextCustomizerFa
@Override
public ContextCustomizer createContextCustomizer(Class> testClass,
List configAttributes) {
+
BeanOverrideParser parser = new BeanOverrideParser();
parseMetadata(testClass, parser);
if (parser.getOverrideMetadata().isEmpty()) {
@@ -56,10 +57,9 @@ private void parseMetadata(Class> testClass, BeanOverrideParser parser) {
}
/**
- * A {@link ContextCustomizer} for Bean Overriding in tests.
+ * {@link ContextCustomizer} for Bean Overriding in tests.
*/
- @Reflective
- static final class BeanOverrideContextCustomizer implements ContextCustomizer {
+ private static final class BeanOverrideContextCustomizer implements ContextCustomizer {
private final Set metadata;
@@ -97,4 +97,5 @@ public int hashCode() {
return this.metadata.hashCode();
}
}
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideParser.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideParser.java
index c4d25b7c596e..1741cbf3d068 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideParser.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideParser.java
@@ -34,23 +34,22 @@
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
+import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.DIRECT;
+
/**
- * A parser that discovers annotations meta-annotated with {@link BeanOverride}
+ * A parser that discovers annotations meta-annotated with {@link BeanOverride @BeanOverride}
* on fields of a given class and creates {@link OverrideMetadata} accordingly.
*
* @author Simon Baslé
+ * @since 6.2
*/
class BeanOverrideParser {
- private final Set parsedMetadata;
+ private final Set parsedMetadata = new LinkedHashSet<>();
- BeanOverrideParser() {
- this.parsedMetadata = new LinkedHashSet<>();
- }
/**
- * Getter for the set of {@link OverrideMetadata} once {@link #parse(Class)}
- * has been called.
+ * Get the set of {@link OverrideMetadata} once {@link #parse(Class)} has been called.
*/
Set getOverrideMetadata() {
return Collections.unmodifiableSet(this.parsedMetadata);
@@ -58,12 +57,12 @@ Set getOverrideMetadata() {
/**
* Discover fields of the provided class that are meta-annotated with
- * {@link BeanOverride}, then instantiate their corresponding
- * {@link BeanOverrideProcessor} and use it to create an {@link OverrideMetadata}
- * instance for each field. Each call to {@code parse} adds the parsed
- * metadata to the parser's override metadata {{@link #getOverrideMetadata()}
- * set}
- * @param testClass the class which fields to inspect
+ * {@link BeanOverride @BeanOverride}, then instantiate the corresponding
+ * {@link BeanOverrideProcessor} and use it to create {@link OverrideMetadata}
+ * for each field.
+ *
Each call to {@code parse} adds the parsed metadata to the parser's
+ * override metadata {@link #getOverrideMetadata() set}.
+ * @param testClass the test class in which to inspect fields
*/
void parse(Class> testClass) {
ReflectionUtils.doWithFields(testClass, field -> parseField(field, testClass));
@@ -71,11 +70,12 @@ void parse(Class> testClass) {
/**
* Check if any field of the provided {@code testClass} is meta-annotated
- * with {@link BeanOverride}.
+ * with {@link BeanOverride @BeanOverride}.
*
This is similar to the initial discovery of fields in {@link #parse(Class)}
* without the heavier steps of instantiating processors and creating
- * {@link OverrideMetadata}, so this method leaves the current state of
- * {@link #getOverrideMetadata()} unchanged.
+ * {@link OverrideMetadata}. Consequently, this method leaves the current
+ * state of the parser's override metadata {@link #getOverrideMetadata() set}
+ * unchanged.
* @param testClass the class which fields to inspect
* @return true if there is a bean override annotation present, false otherwise
* @see #parse(Class)
@@ -86,7 +86,7 @@ boolean hasBeanOverride(Class> testClass) {
if (hasBeanOverride.get()) {
return;
}
- final long count = MergedAnnotations.from(field, MergedAnnotations.SearchStrategy.DIRECT)
+ long count = MergedAnnotations.from(field, DIRECT)
.stream(BeanOverride.class)
.count();
hasBeanOverride.compareAndSet(false, count > 0L);
@@ -97,45 +97,46 @@ boolean hasBeanOverride(Class> testClass) {
private void parseField(Field field, Class> source) {
AtomicBoolean overrideAnnotationFound = new AtomicBoolean();
- MergedAnnotations.from(field, MergedAnnotations.SearchStrategy.DIRECT)
+ MergedAnnotations.from(field, DIRECT)
.stream(BeanOverride.class)
- .map(bo -> {
- MergedAnnotation> a = bo.getMetaSource();
- Assert.notNull(a, "BeanOverride annotation must be meta-present");
- return new AnnotationPair(a.synthesize(), bo);
+ .map(mergedAnnotation -> {
+ MergedAnnotation> metaSource = mergedAnnotation.getMetaSource();
+ Assert.notNull(metaSource, "@BeanOverride annotation must be meta-present");
+ return new AnnotationPair(metaSource.synthesize(), mergedAnnotation);
})
.forEach(pair -> {
- BeanOverride metaAnnotation = pair.metaAnnotation().synthesize();
- final BeanOverrideProcessor processor = getProcessorInstance(metaAnnotation.value());
+ BeanOverride beanOverride = pair.mergedAnnotation().synthesize();
+ BeanOverrideProcessor processor = getProcessorInstance(beanOverride.value());
if (processor == null) {
return;
}
ResolvableType typeToOverride = processor.getOrDeduceType(field, pair.annotation(), source);
Assert.state(overrideAnnotationFound.compareAndSet(false, true),
- "Multiple bean override annotations found on annotated field <" + field + ">");
+ () -> "Multiple @BeanOverride annotations found on field: " + field);
OverrideMetadata metadata = processor.createMetadata(field, pair.annotation(), typeToOverride);
boolean isNewDefinition = this.parsedMetadata.add(metadata);
Assert.state(isNewDefinition, () -> "Duplicate " + metadata.getBeanOverrideDescription() +
- " overrideMetadata " + metadata);
+ " OverrideMetadata: " + metadata);
});
}
@Nullable
private BeanOverrideProcessor getProcessorInstance(Class extends BeanOverrideProcessor> processorClass) {
- final Constructor extends BeanOverrideProcessor> constructor = ClassUtils.getConstructorIfAvailable(processorClass);
+ Constructor extends BeanOverrideProcessor> constructor = ClassUtils.getConstructorIfAvailable(processorClass);
if (constructor != null) {
- ReflectionUtils.makeAccessible(constructor);
try {
+ ReflectionUtils.makeAccessible(constructor);
return constructor.newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
- throw new BeanDefinitionValidationException("Could not get an instance of BeanOverrideProcessor", ex);
+ throw new BeanDefinitionValidationException(
+ "Failed to instantiate BeanOverrideProcessor of type " + processorClass.getName(), ex);
}
}
return null;
}
- private record AnnotationPair(Annotation annotation, MergedAnnotation metaAnnotation) {}
+ private record AnnotationPair(Annotation annotation, MergedAnnotation mergedAnnotation) {}
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideProcessor.java
index 3019754c3011..b3152ee2cb85 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideProcessor.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideProcessor.java
@@ -22,14 +22,13 @@
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.ResolvableType;
-import org.springframework.core.annotation.MergedAnnotation;
/**
- * An interface for Bean Overriding concrete processing.
+ * Strategy interface for Bean Override processing.
*
*
Processors are generally linked to one or more specific concrete annotations
- * (meta-annotated with {@link BeanOverride}) and specify different steps in the
- * process of parsing these annotations, ultimately creating
+ * (meta-annotated with {@link BeanOverride @BeanOverride}) and specify different
+ * steps in the process of parsing these annotations, ultimately creating
* {@link OverrideMetadata} which will be used to instantiate the overrides.
*
*
Implementations are required to have a no-argument constructor and be
@@ -42,30 +41,31 @@
public interface BeanOverrideProcessor {
/**
- * Determine a {@link ResolvableType} for which an {@link OverrideMetadata}
- * instance will be created, e.g. by using the annotation to determine the
- * type.
- *
Defaults to the field corresponding {@link ResolvableType},
- * additionally tracking the source class if the field is a {@link TypeVariable}.
+ * Determine the {@link ResolvableType} for which an {@link OverrideMetadata}
+ * instance will be created — for example, by using the supplied annotation
+ * to determine the type.
+ *
The default implementation deduces the field's corresponding
+ * {@link ResolvableType}, additionally tracking the source class if the
+ * field's type is a {@link TypeVariable}.
*/
default ResolvableType getOrDeduceType(Field field, Annotation annotation, Class> source) {
- return (field.getGenericType() instanceof TypeVariable) ? ResolvableType.forField(field, source)
- : ResolvableType.forField(field);
+ return (field.getGenericType() instanceof TypeVariable ?
+ ResolvableType.forField(field, source) : ResolvableType.forField(field));
}
/**
- * Create an {@link OverrideMetadata} for a given annotated field and target
- * {@link #getOrDeduceType(Field, Annotation, Class) type}.
- * Specific implementations of metadata can have state to be used during
- * override {@link OverrideMetadata#createOverride(String, BeanDefinition,
- * Object) instance creation}, that is from further parsing the annotation or
- * the annotated field.
+ * Create an {@link OverrideMetadata} instance for the given annotated field
+ * and target {@link #getOrDeduceType(Field, Annotation, Class) type}.
+ *
Specific implementations of metadata can have state to be used during
+ * override {@linkplain OverrideMetadata#createOverride(String, BeanDefinition,
+ * Object) instance creation} — for example, from further parsing of the
+ * annotation or the annotated field.
* @param field the annotated field
* @param overrideAnnotation the field annotation
* @param typeToOverride the target type
- * @return a new {@link OverrideMetadata}
+ * @return a new {@link OverrideMetadata} instance
* @see #getOrDeduceType(Field, Annotation, Class)
- * @see MergedAnnotation#synthesize()
*/
OverrideMetadata createMetadata(Field field, Annotation overrideAnnotation, ResolvableType typeToOverride);
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideStrategy.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideStrategy.java
index bb030b2a583b..2c51a475d38b 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideStrategy.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideStrategy.java
@@ -17,7 +17,7 @@
package org.springframework.test.bean.override;
/**
- * Strategies for override instantiation, implemented in
+ * Strategies for bean override instantiation, implemented in
* {@link BeanOverrideBeanPostProcessor}.
*
* @author Simon Baslé
@@ -26,22 +26,22 @@
public enum BeanOverrideStrategy {
/**
- * Replace a given bean definition, immediately preparing a singleton
- * instance. Enforces the original bean definition to exist.
+ * Replace a given bean definition, immediately preparing a singleton instance.
+ *
Requires that the original bean definition exists.
*/
REPLACE_DEFINITION,
/**
- * Replace a given bean definition, immediately preparing a singleton
- * instance. If the original bean definition does not exist, create the
- * override definition instead of failing.
+ * Replace a given bean definition, immediately preparing a singleton instance.
+ *
If the original bean definition does not exist, an override definition
+ * will be created instead of failing.
*/
REPLACE_OR_CREATE_DEFINITION,
/**
- * Intercept and wrap the actual bean instance upon creation, during
- * {@link org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(Object, String)
- * early bean definition}.
+ * Intercept and wrap the actual bean instance upon creation, during the {@linkplain
+ * org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(Object, String)
+ * early bean reference} phase.
*/
WRAP_EARLY_BEAN
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideTestExecutionListener.java
index a2b5a7f1d5d6..91dd9760b62e 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/BeanOverrideTestExecutionListener.java
@@ -20,17 +20,17 @@
import java.util.function.BiConsumer;
import org.springframework.test.context.TestContext;
-import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.util.ReflectionUtils;
/**
- * A {@link TestExecutionListener} implementation that enables Bean Override
- * support in tests, injecting overridden beans in appropriate fields.
+ * {@code TestExecutionListener} that enables Bean Override support in tests,
+ * injecting overridden beans in appropriate fields of the test instance.
*
*
Some flavors of Bean Override might additionally require the use of
- * additional listeners, which should be mentioned in the annotation(s) javadoc.
+ * additional listeners, which should be mentioned in the javadoc for the
+ * corresponding annotations.
*
* @author Simon Baslé
* @since 6.2
@@ -68,36 +68,42 @@ protected void injectFields(TestContext testContext) {
/**
* Using a registered {@link BeanOverrideBeanPostProcessor}, find metadata
* associated with the current test class and ensure fields are nulled out
- * then re-injected with the overridden bean instance. This method does
- * nothing if the {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
- * attribute is not present in the {@code testContext}.
+ * and then re-injected with the overridden bean instance.
+ *
This method does nothing if the
+ * {@link DependencyInjectionTestExecutionListener#REINJECT_DEPENDENCIES_ATTRIBUTE}
+ * attribute is not present in the {@code TestContext}.
*/
protected void reinjectFieldsIfConfigured(final TestContext testContext) throws Exception {
if (Boolean.TRUE.equals(
testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) {
+
postProcessFields(testContext, (testMetadata, postProcessor) -> {
- Field f = testMetadata.overrideMetadata.field();
- ReflectionUtils.makeAccessible(f);
- ReflectionUtils.setField(f, testMetadata.testInstance(), null);
- postProcessor.inject(f, testMetadata.testInstance(), testMetadata.overrideMetadata());
+ Object testInstance = testMetadata.testInstance;
+ Field field = testMetadata.overrideMetadata.field();
+ ReflectionUtils.makeAccessible(field);
+ ReflectionUtils.setField(field, testInstance, null);
+ postProcessor.inject(field, testInstance, testMetadata.overrideMetadata);
});
}
}
private void postProcessFields(TestContext testContext, BiConsumer consumer) {
- //avoid full parsing but validate that this particular class has some bean override field(s)
+
+ Class> testClass = testContext.getTestClass();
+ Object testInstance = testContext.getTestInstance();
BeanOverrideParser parser = new BeanOverrideParser();
- if (parser.hasBeanOverride(testContext.getTestClass())) {
- BeanOverrideBeanPostProcessor postProcessor = testContext.getApplicationContext()
- .getBean(BeanOverrideBeanPostProcessor.class);
- // the class should have already been parsed by the context customizer
- for (OverrideMetadata metadata: postProcessor.getOverrideMetadata()) {
- if (!metadata.field().getDeclaringClass().equals(testContext.getTestClass())) {
+
+ // Avoid full parsing, but validate that this particular class has some bean override field(s).
+ if (parser.hasBeanOverride(testClass)) {
+ BeanOverrideBeanPostProcessor postProcessor =
+ testContext.getApplicationContext().getBean(BeanOverrideBeanPostProcessor.class);
+ // The class should have already been parsed by the context customizer.
+ for (OverrideMetadata metadata : postProcessor.getOverrideMetadata()) {
+ if (!metadata.field().getDeclaringClass().equals(testClass)) {
continue;
}
- consumer.accept(new TestContextOverrideMetadata(testContext.getTestInstance(), metadata),
- postProcessor);
+ consumer.accept(new TestContextOverrideMetadata(testInstance, metadata), postProcessor);
}
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/OverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/bean/override/OverrideMetadata.java
index d76f550adf40..ff2e693a5c17 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/OverrideMetadata.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/OverrideMetadata.java
@@ -23,6 +23,7 @@
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.ResolvableType;
+import org.springframework.core.style.ToStringCreator;
import org.springframework.lang.Nullable;
/**
@@ -41,24 +42,27 @@ public abstract class OverrideMetadata {
private final BeanOverrideStrategy strategy;
+
protected OverrideMetadata(Field field, Annotation overrideAnnotation,
ResolvableType typeToOverride, BeanOverrideStrategy strategy) {
+
this.field = field;
this.overrideAnnotation = overrideAnnotation;
this.typeToOverride = typeToOverride;
this.strategy = strategy;
}
+
/**
- * Return a short human-readable description of the kind of override this
+ * Return a short, human-readable description of the kind of override this
* instance handles.
*/
public abstract String getBeanOverrideDescription();
/**
- * Return the expected bean name to override. Typically, this is either
- * explicitly set in the concrete annotations or defined by the annotated
- * field's name.
+ * Return the expected bean name to override.
+ *
Typically, this is either explicitly set in a concrete annotation or
+ * inferred from the annotated field's name.
* @return the expected bean name
*/
protected String getExpectedBeanName() {
@@ -74,7 +78,7 @@ public Field field() {
/**
* Return the concrete override annotation, that is the one meta-annotated
- * with {@link BeanOverride}.
+ * with {@link BeanOverride @BeanOverride}.
*/
public Annotation overrideAnnotation() {
return this.overrideAnnotation;
@@ -103,21 +107,21 @@ public final BeanOverrideStrategy getBeanOverrideStrategy() {
* @param existingBeanDefinition an existing bean definition for that bean
* name, or {@code null} if not relevant
* @param existingBeanInstance an existing instance for that bean name,
- * for wrapping purpose, or {@code null} if irrelevant
+ * for wrapping purposes, or {@code null} if irrelevant
* @return the instance with which to override the bean
*/
protected abstract Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
@Nullable Object existingBeanInstance);
/**
- * Optionally track objects created by this {@link OverrideMetadata}
- * (default is no tracking).
+ * Optionally track objects created by this {@link OverrideMetadata}.
+ *
The default is not to track, but this can be overridden in subclasses.
* @param override the bean override instance to track
- * @param trackingBeanRegistry the registry in which trackers could
+ * @param trackingBeanRegistry the registry in which trackers can
* optionally be registered
*/
protected void track(Object override, SingletonBeanRegistry trackingBeanRegistry) {
- //NO-OP
+ // NO-OP
}
@Override
@@ -132,21 +136,23 @@ public boolean equals(Object obj) {
return Objects.equals(this.field, that.field) &&
Objects.equals(this.overrideAnnotation, that.overrideAnnotation) &&
Objects.equals(this.strategy, that.strategy) &&
- Objects.equals(this.typeToOverride, that.typeToOverride);
+ Objects.equals(typeToOverride(), that.typeToOverride());
}
@Override
public int hashCode() {
- return Objects.hash(this.field, this.overrideAnnotation, this.strategy, this.typeToOverride);
+ return Objects.hash(this.field, this.overrideAnnotation, this.strategy, typeToOverride());
}
@Override
public String toString() {
- return "OverrideMetadata[" +
- "category=" + this.getBeanOverrideDescription() + ", " +
- "field=" + this.field + ", " +
- "overrideAnnotation=" + this.overrideAnnotation + ", " +
- "strategy=" + this.strategy + ", " +
- "typeToOverride=" + this.typeToOverride + ']';
+ return new ToStringCreator(this)
+ .append("category", getBeanOverrideDescription())
+ .append("field", this.field)
+ .append("overrideAnnotation", this.overrideAnnotation)
+ .append("strategy", this.strategy)
+ .append("typeToOverride", typeToOverride())
+ .toString();
}
+
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/Definition.java b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/Definition.java
index 57ad9c26e837..42bc1c3885e1 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/Definition.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/Definition.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,10 +32,12 @@
* Base class for {@link MockDefinition} and {@link SpyDefinition}.
*
* @author Phillip Webb
+ * @since 6.2
*/
abstract class Definition extends OverrideMetadata {
- static final int MULTIPLIER = 31;
+ protected static final int MULTIPLIER = 31;
+
protected final String name;
@@ -43,14 +45,17 @@ abstract class Definition extends OverrideMetadata {
private final boolean proxyTargetAware;
+
Definition(String name, @Nullable MockReset reset, boolean proxyTargetAware, Field field,
Annotation annotation, ResolvableType typeToOverride, BeanOverrideStrategy strategy) {
+
super(field, annotation, typeToOverride, strategy);
this.name = name;
this.reset = (reset != null) ? reset : MockReset.AFTER;
this.proxyTargetAware = proxyTargetAware;
}
+
@Override
protected String getExpectedBeanName() {
if (StringUtils.hasText(this.name)) {
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockDefinition.java b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockDefinition.java
index 05fea8394959..19f03b51540b 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockDefinition.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockDefinition.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 the original author or authors.
+ * Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,17 +42,17 @@
* A complete definition that can be used to create a Mockito mock.
*
* @author Phillip Webb
+ * @since 6.2
*/
class MockDefinition extends Definition {
- private static final int MULTIPLIER = 31;
-
private final Set> extraInterfaces;
private final Answers answer;
private final boolean serializable;
+
MockDefinition(MockitoBean annotation, Field field, ResolvableType typeToMock) {
this(annotation.name(), annotation.reset(), field, annotation, typeToMock,
annotation.extraInterfaces(), annotation.answers(), annotation.serializable());
@@ -60,6 +60,7 @@ class MockDefinition extends Definition {
MockDefinition(String name, MockReset reset, Field field, Annotation annotation, ResolvableType typeToMock,
Class>[] extraInterfaces, @Nullable Answers answer, boolean serializable) {
+
super(name, reset, false, field, annotation, typeToMock, BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION);
Assert.notNull(typeToMock, "TypeToMock must not be null");
this.extraInterfaces = asClassSet(extraInterfaces);
@@ -67,6 +68,7 @@ class MockDefinition extends Definition {
this.serializable = serializable;
}
+
@Override
public String getBeanOverrideDescription() {
return "mock";
@@ -119,7 +121,7 @@ public boolean equals(@Nullable Object obj) {
}
MockDefinition other = (MockDefinition) obj;
boolean result = super.equals(obj);
- result = result && ObjectUtils.nullSafeEquals(this.typeToOverride(), other.typeToOverride());
+ result = result && ObjectUtils.nullSafeEquals(typeToOverride(), other.typeToOverride());
result = result && ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces);
result = result && ObjectUtils.nullSafeEquals(this.answer, other.answer);
result = result && this.serializable == other.serializable;
@@ -129,7 +131,7 @@ public boolean equals(@Nullable Object obj) {
@Override
public int hashCode() {
int result = super.hashCode();
- result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.typeToOverride());
+ result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(typeToOverride());
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces);
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer);
result = MULTIPLIER * result + Boolean.hashCode(this.serializable);
@@ -138,13 +140,14 @@ public int hashCode() {
@Override
public String toString() {
- return new ToStringCreator(this).append("name", this.name)
- .append("typeToMock", this.typeToOverride())
- .append("extraInterfaces", this.extraInterfaces)
- .append("answer", this.answer)
- .append("serializable", this.serializable)
- .append("reset", getReset())
- .toString();
+ return new ToStringCreator(this)
+ .append("name", this.name)
+ .append("typeToMock", typeToOverride())
+ .append("extraInterfaces", this.extraInterfaces)
+ .append("answer", this.answer)
+ .append("serializable", this.serializable)
+ .append("reset", getReset())
+ .toString();
}
T createMock() {
@@ -164,7 +167,7 @@ T createMock(String name) {
if (this.serializable) {
settings.serializable();
}
- return (T) mock(this.typeToOverride().resolve(), settings);
+ return (T) mock(typeToOverride().resolve(), settings);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockReset.java b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockReset.java
index e5d54c79ebe8..51b2dbed90bd 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockReset.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockReset.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 the original author or authors.
+ * Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@ public enum MockReset {
*/
NONE;
+
/**
* Create {@link MockSettings settings} to be used with mocks where reset
* should occur before each test method runs.
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBean.java b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBean.java
index 83a50faf7608..c95ed9dba92f 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBean.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBean.java
@@ -34,12 +34,14 @@
* a new one will be added to the context.
*
*
Dependencies that are known to the application context but are not beans
- * (such as those {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
- * registered directly}) will not be found and a mocked bean will be added to
+ * (such as those
+ * {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
+ * registered directly}) will not be found, and a mocked bean will be added to
* the context alongside the existing dependency.
*
* @author Simon Baslé
* @since 6.2
+ * @see MockitoSpyBean
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@@ -48,35 +50,38 @@
public @interface MockitoBean {
/**
- * The name of the bean to register or replace. If not specified, it will be
- * the name of the annotated field.
- * @return the name of the bean
+ * The name of the bean to register or replace.
+ *
If not specified, the name of the annotated field will be used.
+ * @return the name of the mocked bean
*/
String name() default "";
/**
- * Any extra interfaces that should also be declared on the mock.
- * See {@link MockSettings#extraInterfaces(Class...)} for details.
+ * Extra interfaces that should also be declared on the mock.
+ *
Defaults to none.
* @return any extra interfaces
+ * @see MockSettings#extraInterfaces(Class...)
*/
Class>[] extraInterfaces() default {};
/**
* The {@link Answers} type to use on the mock.
+ *
Defaults to {@link Answers#RETURNS_DEFAULTS}.
* @return the answer type
*/
Answers answers() default Answers.RETURNS_DEFAULTS;
/**
- * If the generated mock is serializable.
- * See {@link MockSettings#serializable()} for details.
- * @return if the mock is serializable
+ * Whether the generated mock is serializable.
+ *
Defaults to {@code false}.
+ * @return {@code true} if the mock is serializable
+ * @see MockSettings#serializable()
*/
boolean serializable() default false;
/**
- * The reset mode to apply to the mock bean.
- * The default is {@link MockReset#AFTER} meaning that mocks are
+ * The reset mode to apply to the mock.
+ *
The default is {@link MockReset#AFTER} meaning that mocks are
* automatically reset after each test method is invoked.
* @return the reset mode
*/
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeanOverrideProcessor.java
index e82a24b7e96f..83ab8c8499d1 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeanOverrideProcessor.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeanOverrideProcessor.java
@@ -40,7 +40,8 @@ public OverrideMetadata createMetadata(Field field, Annotation overrideAnnotatio
else if (overrideAnnotation instanceof MockitoSpyBean spyBean) {
return new SpyDefinition(spyBean, field, typeToMock);
}
- throw new IllegalArgumentException("Invalid annotation for MockitoBeanOverrideProcessor: " + overrideAnnotation.getClass().getName());
+ throw new IllegalArgumentException("Invalid annotation for MockitoBeanOverrideProcessor: " +
+ overrideAnnotation.getClass().getName());
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeans.java b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeans.java
index 9431b4e872dc..7f596b6d7683 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeans.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/mockito/MockitoBeans.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,11 +24,13 @@
* Beans created using Mockito.
*
* @author Andy Wilkinson
+ * @since 6.2
*/
class MockitoBeans implements Iterable