Polish
This commit is contained in:
parent
6b64e9992a
commit
8172d7adfe
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
* <p>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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
* <p>Some flavors of Bean Override might additionally require the use of
|
||||
* additional listeners, which should be mentioned in the annotation(s) javadoc.
|
||||
|
|
|
|||
|
|
@ -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) &&
|
||||
|
|
|
|||
|
|
@ -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}.
|
||||
*
|
||||
* <p>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:
|
||||
* <p>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:
|
||||
* <ul>
|
||||
* <li>if the annotation's {@link #methodName()} is specified,
|
||||
* look for that one.</li>
|
||||
* <li>if not, look for exactly one method named with the
|
||||
* {@link #CONVENTION_SUFFIX} suffix and either:</li>
|
||||
* <ul>
|
||||
* <li>starting with the annotated field name</li>
|
||||
* <li>starting with the bean name</li>
|
||||
* </ul>
|
||||
* <li>if the {@link #methodName()} is specified, look for a static method with
|
||||
* that name.</li>
|
||||
* <li>if not, look for exactly one static method named with a suffix equal to
|
||||
* {@value #CONVENTION_SUFFIX} and either starting with the annotated field
|
||||
* name, or starting with the bean name.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>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.
|
||||
* <p>Consider the following example:
|
||||
*
|
||||
* <pre><code>
|
||||
* class CustomerServiceTests {
|
||||
*
|
||||
* @TestBean
|
||||
* private CustomerRepository repository;
|
||||
*
|
||||
* // Tests
|
||||
*
|
||||
* private static CustomerRepository repositoryTestOverride() {
|
||||
* return new TestCustomerRepository();
|
||||
* }
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>To make things more explicit, the method name can be set, as shown in the
|
||||
* following example:
|
||||
*
|
||||
* <pre><code>
|
||||
* class CustomerServiceTests {
|
||||
*
|
||||
* @TestBean(methodName = "createTestCustomerRepository")
|
||||
* private CustomerRepository repository;
|
||||
*
|
||||
* // Tests
|
||||
*
|
||||
* private static CustomerRepository createTestCustomerRepository() {
|
||||
* return new TestCustomerRepository();
|
||||
* }
|
||||
* }</code></pre>
|
||||
*
|
||||
* <p>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.
|
||||
* <p> 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.
|
||||
* <p>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.
|
||||
* <p>Default to {@code ""} (the empty String) to use the name of the
|
||||
* annotated field.
|
||||
*/
|
||||
String name() default "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String> expectedNames = new LinkedHashSet<>(Arrays.asList(expectedMethodNames));
|
||||
final List<Method> found = Arrays.stream(enclosingClass.getDeclaredMethods())
|
||||
.filter(m -> Modifier.isStatic(m.getModifiers()))
|
||||
.filter(m -> expectedNames.contains(m.getName()) && expectedMethodReturnType
|
||||
.isAssignableFrom(m.getReturnType()))
|
||||
.collect(Collectors.toList());
|
||||
.filter(method -> Modifier.isStatic(method.getModifiers()))
|
||||
.filter(method -> expectedNames.contains(method.getName())
|
||||
&& expectedMethodReturnType.isAssignableFrom(method.getReturnType()))
|
||||
.toList();
|
||||
|
||||
Assert.state(found.size() == 1, () -> "Found " + found.size() + " static methods " +
|
||||
"instead of exactly one, matching a name in " + expectedNames + " with return type " +
|
||||
|
|
@ -87,7 +87,7 @@ public class TestBeanOverrideProcessor implements BeanOverrideProcessor {
|
|||
}
|
||||
// otherwise defer the resolution of the static method until OverrideMetadata#createOverride
|
||||
return new MethodConventionOverrideMetadata(field, null, null, overrideAnnotation,
|
||||
typeToOverride);
|
||||
typeToOverride);
|
||||
}
|
||||
|
||||
static final class MethodConventionOverrideMetadata extends OverrideMetadata {
|
||||
|
|
|
|||
Loading…
Reference in New Issue