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 5a2d4acb3ac..c4d25b7c596 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 @@ -100,12 +100,12 @@ class BeanOverrideParser { MergedAnnotations.from(field, MergedAnnotations.SearchStrategy.DIRECT) .stream(BeanOverride.class) .map(bo -> { - var a = bo.getMetaSource(); + MergedAnnotation> a = bo.getMetaSource(); Assert.notNull(a, "BeanOverride annotation must be meta-present"); return new AnnotationPair(a.synthesize(), bo); }) .forEach(pair -> { - var metaAnnotation = pair.metaAnnotation().synthesize(); + BeanOverride metaAnnotation = pair.metaAnnotation().synthesize(); final BeanOverrideProcessor processor = getProcessorInstance(metaAnnotation.value()); if (processor == null) { return; 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 c4621737502..3019754c301 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 @@ -26,7 +26,8 @@ import org.springframework.core.annotation.MergedAnnotation; /** * An interface for Bean Overriding concrete processing. - * Processors are generally linked to one or more specific concrete annotations + * + *
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 * {@link OverrideMetadata} which will be used to instantiate the overrides. @@ -57,8 +58,8 @@ public interface BeanOverrideProcessor { * {@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} (e.g. from further parsing the annotation or - * the annotated field). + * Object) instance creation}, that is from further parsing the annotation or + * the annotated field. * @param field the annotated field * @param overrideAnnotation the field annotation * @param typeToOverride the target type 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 32bf431495e..bb030b2a583 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 @@ -26,20 +26,23 @@ package org.springframework.test.bean.override; public enum BeanOverrideStrategy { /** - * Replace a given bean's definition, immediately preparing a singleton + * Replace a given bean definition, immediately preparing a singleton * instance. Enforces the original bean definition to exist. */ REPLACE_DEFINITION, + /** - * Replace a given bean's definition, immediately preparing a singleton + * 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_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}. */ - WRAP_EARLY_BEAN; + 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 dc86a686cad..a2b5a7f1d5d 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 @@ -26,8 +26,8 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution import org.springframework.util.ReflectionUtils; /** - * A {@link TestExecutionListener} that enables Bean Override support in - * tests, injecting overridden beans in appropriate fields. + * A {@link TestExecutionListener} implementation that enables Bean Override + * support in tests, injecting overridden beans in appropriate fields. * *
Some flavors of Bean Override might additionally require the use of * additional listeners, which should be mentioned in the annotation(s) javadoc. 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 4261441bef6..d76f550adf4 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 @@ -34,11 +34,14 @@ import org.springframework.lang.Nullable; public abstract class OverrideMetadata { private final Field field; + private final Annotation overrideAnnotation; + private final ResolvableType typeToOverride; + private final BeanOverrideStrategy strategy; - public OverrideMetadata(Field field, Annotation overrideAnnotation, + protected OverrideMetadata(Field field, Annotation overrideAnnotation, ResolvableType typeToOverride, BeanOverrideStrategy strategy) { this.field = field; this.overrideAnnotation = overrideAnnotation; @@ -47,50 +50,46 @@ public abstract class OverrideMetadata { } /** - * Define a short human-readable description of the kind of override this - * OverrideMetadata is about. This is especially useful for - * {@link BeanOverrideProcessor} that produce several subtypes of metadata - * (e.g. "mock" vs "spy"). + * Return a short human-readable description of the kind of override this + * instance handles. */ public abstract String getBeanOverrideDescription(); /** - * Provide the expected bean name to override. Typically, this is either + * 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, not null + * @return the expected bean name */ protected String getExpectedBeanName() { return this.field.getName(); } /** - * The field annotated with a {@link BeanOverride}-compatible annotation. - * @return the annotated field + * Return the annotated {@link Field}. */ public Field field() { return this.field; } /** - * The concrete override annotation, i.e. the one meta-annotated with - * {@link BeanOverride}. + * Return the concrete override annotation, that is the one meta-annotated + * with {@link BeanOverride}. */ public Annotation overrideAnnotation() { return this.overrideAnnotation; } /** - * The type to override, as a {@link ResolvableType}. + * Return the bean {@link ResolvableType type} to override. */ public ResolvableType typeToOverride() { return this.typeToOverride; } /** - * Define the broad {@link BeanOverrideStrategy} for this - * {@link OverrideMetadata}, as a hint on how and when the override instance - * should be created. + * Return the {@link BeanOverrideStrategy} for this instance, as a hint on + * how and when the override instance should be created. */ public final BeanOverrideStrategy getBeanOverrideStrategy() { return this.strategy; @@ -99,7 +98,7 @@ public abstract class OverrideMetadata { /** * Create an override instance from this {@link OverrideMetadata}, * optionally provided with an existing {@link BeanDefinition} and/or an - * original instance (i.e. a singleton or an early wrapped instance). + * original instance, that is a singleton or an early wrapped instance. * @param beanName the name of the bean being overridden * @param existingBeanDefinition an existing bean definition for that bean * name, or {@code null} if not relevant @@ -129,7 +128,7 @@ public abstract class OverrideMetadata { if (obj == null || !getClass().isAssignableFrom(obj.getClass())) { return false; } - var that = (OverrideMetadata) obj; + OverrideMetadata that = (OverrideMetadata) obj; return Objects.equals(this.field, that.field) && Objects.equals(this.overrideAnnotation, that.overrideAnnotation) && Objects.equals(this.strategy, that.strategy) && diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBean.java b/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBean.java index d5d74214138..85402eab767 100644 --- a/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBean.java +++ b/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBean.java @@ -25,29 +25,63 @@ import java.lang.annotation.Target; import org.springframework.test.bean.override.BeanOverride; /** - * Mark a field to represent a "method" bean override of the bean of the same - * name and inject the field with the overriding instance. + * Mark a field to override a bean instance in the {@code BeanFactory}. * - *
The instance is created from a static method in the declaring class which - * return type is compatible with the annotated field and which name follows the - * convention: + *
The instance is created from a no-arg static method in the declaring + * class whose return type is compatible with the annotated field. The method + * is deduced as follows: *
The annotated field's name is interpreted to be the name of the original - * bean to override, unless the annotation's {@link #name()} is specified. + *
Consider the following example: + * + *
+ * class CustomerServiceTests {
+ *
+ * @TestBean
+ * private CustomerRepository repository;
+ *
+ * // Tests
+ *
+ * private static CustomerRepository repositoryTestOverride() {
+ * return new TestCustomerRepository();
+ * }
+ * }
+ *
+ * In the example above, the {@code repository} bean is replaced by the + * instance generated by the {@code repositoryTestOverride} method. Not only + * the overridden instance is injected in the {@code repository} field, but it + * is also replaced in the {@code BeanFactory} so that other injection points + * for that bean use the override. + * + *
To make things more explicit, the method name can be set, as shown in the + * following example: + * + *
+ * class CustomerServiceTests {
+ *
+ * @TestBean(methodName = "createTestCustomerRepository")
+ * private CustomerRepository repository;
+ *
+ * // Tests
+ *
+ * private static CustomerRepository createTestCustomerRepository() {
+ * return new TestCustomerRepository();
+ * }
+ * }
+ *
+ * By default, the name of the bean is inferred from the name of the annotated + * field. To use a different bean name, set the {@link #name()} property. * - * @see TestBeanOverrideProcessor * @author Simon Baslé + * @author Stephane Nicoll * @since 6.2 + * @see TestBeanOverrideProcessor */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @@ -56,23 +90,23 @@ import org.springframework.test.bean.override.BeanOverride; public @interface TestBean { /** - * The method suffix expected as a convention in static methods which - * provides an override instance. + * Required suffix for a method that overrides a bean instance that is + * detected by convention. */ String CONVENTION_SUFFIX = "TestOverride"; /** - * The name of a static method to look for in the Configuration, which will - * be used to instantiate the override bean and inject the annotated field. - *
Default is {@code ""} (the empty String), which is translated into - * the annotated field's name concatenated with the - * {@link #CONVENTION_SUFFIX}. + * Name of a static method to look for in the test, which will be used to + * instantiate the bean to override. + *
Default to {@code ""} (the empty String), which detects the method + * to us by convention. */ String methodName() default ""; /** - * The name of the original bean to override, or {@code ""} (the empty - * String) to deduce the name from the annotated field. + * Name of the bean to override. + *
Default to {@code ""} (the empty String) to use the name of the
+ * annotated field.
*/
String name() default "";
}
diff --git a/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBeanOverrideProcessor.java
index 20eb05fb166..f2b659b0be1 100644
--- a/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBeanOverrideProcessor.java
+++ b/spring-test/src/main/java/org/springframework/test/bean/override/convention/TestBeanOverrideProcessor.java
@@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.ResolvableType;
@@ -37,8 +36,8 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
- * Simple {@link BeanOverrideProcessor} primarily made to work with the
- * {@link TestBean} annotation but can work with arbitrary override annotations
+ * {@link BeanOverrideProcessor} implementation primarily made to work with
+ * {@link TestBean @TestBean}, but can work with arbitrary override annotations
* provided the annotated class has a relevant method according to the
* convention documented in {@link TestBean}.
*
@@ -48,19 +47,20 @@ import org.springframework.util.StringUtils;
public class TestBeanOverrideProcessor implements BeanOverrideProcessor {
/**
- * Ensures the {@code enclosingClass} has a static, no-arguments method with
- * the provided {@code expectedMethodReturnType} and exactly one of the
+ * Ensure the given {@code enclosingClass} has a static, no-arguments method
+ * with the given {@code expectedMethodReturnType} and exactly one of the
* {@code expectedMethodNames}.
*/
public static Method ensureMethod(Class> enclosingClass, Class> expectedMethodReturnType,
String... expectedMethodNames) {
+
Assert.isTrue(expectedMethodNames.length > 0, "At least one expectedMethodName is required");
Set