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