Skip to content

Commit

Permalink
Support non-public BeanOverrideProcessors in the TestContext framework
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Mar 18, 2024
1 parent 6df2764 commit 0ef5a40
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,17 @@
package org.springframework.test.context.bean.override;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.springframework.beans.factory.support.BeanDefinitionValidationException;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.DIRECT;
Expand All @@ -41,6 +37,7 @@
* on fields of a given class and creates {@link OverrideMetadata} accordingly.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class BeanOverrideParser {
Expand Down Expand Up @@ -102,41 +99,21 @@ private void parseField(Field field, Class<?> source) {
.map(mergedAnnotation -> {
MergedAnnotation<?> metaSource = mergedAnnotation.getMetaSource();
Assert.notNull(metaSource, "@BeanOverride annotation must be meta-present");
return new AnnotationPair(metaSource.synthesize(), mergedAnnotation);
return new AnnotationPair(metaSource.synthesize(), mergedAnnotation.synthesize());
})
.forEach(pair -> {
BeanOverride beanOverride = pair.mergedAnnotation().synthesize();
BeanOverrideProcessor processor = getProcessorInstance(beanOverride.value());
if (processor == null) {
return;
}
ResolvableType typeToOverride = processor.getOrDeduceType(field, pair.annotation(), source);
BeanOverrideProcessor processor = BeanUtils.instantiateClass(pair.beanOverride.value());
ResolvableType typeToOverride = processor.getOrDeduceType(field, pair.composedAnnotation, source);

Assert.state(overrideAnnotationFound.compareAndSet(false, true),
() -> "Multiple @BeanOverride annotations found on field: " + field);
OverrideMetadata metadata = processor.createMetadata(field, pair.annotation(), typeToOverride);
OverrideMetadata metadata = processor.createMetadata(field, pair.composedAnnotation, typeToOverride);
boolean isNewDefinition = this.parsedMetadata.add(metadata);
Assert.state(isNewDefinition, () -> "Duplicate " + metadata.getBeanOverrideDescription() +
" OverrideMetadata: " + metadata);
});
}

@Nullable
private BeanOverrideProcessor getProcessorInstance(Class<? extends BeanOverrideProcessor> processorClass) {
Constructor<? extends BeanOverrideProcessor> constructor = ClassUtils.getConstructorIfAvailable(processorClass);
if (constructor != null) {
try {
ReflectionUtils.makeAccessible(constructor);
return constructor.newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
throw new BeanDefinitionValidationException(
"Failed to instantiate BeanOverrideProcessor of type " + processorClass.getName(), ex);
}
}
return null;
}

private record AnnotationPair(Annotation annotation, MergedAnnotation<BeanOverride> mergedAnnotation) {}
private record AnnotationPair(Annotation composedAnnotation, BeanOverride beanOverride) {}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
import static org.springframework.test.context.bean.override.example.ExampleBeanOverrideProcessor.DUPLICATE_TRIGGER;

/**
* Unit tests for {@link BeanOverrideParser}.
Expand All @@ -35,6 +34,9 @@
*/
class BeanOverrideParserTests {

// Copy of ExampleBeanOverrideProcessor.DUPLICATE_TRIGGER which is package-private.
private static final String DUPLICATE_TRIGGER = "DUPLICATE";

private final BeanOverrideParser parser = new BeanOverrideParser();


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
import org.springframework.test.context.bean.override.BeanOverrideProcessor;
import org.springframework.test.context.bean.override.OverrideMetadata;

public class ExampleBeanOverrideProcessor implements BeanOverrideProcessor {
// Intentionally NOT public
class ExampleBeanOverrideProcessor implements BeanOverrideProcessor {

static final String DUPLICATE_TRIGGER = "DUPLICATE";

private static final TestOverrideMetadata CONSTANT = new TestOverrideMetadata() {
@Override
Expand All @@ -32,8 +35,6 @@ public String toString() {
}
};

public static final String DUPLICATE_TRIGGER = "CONSTANT";

@Override
public OverrideMetadata createMetadata(Field field, Annotation overrideAnnotation, ResolvableType typeToOverride) {
if (!(overrideAnnotation instanceof ExampleBeanOverrideAnnotation annotation)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

import static org.springframework.test.context.bean.override.example.ExampleBeanOverrideAnnotation.DEFAULT_VALUE;

public class TestOverrideMetadata extends OverrideMetadata {
class TestOverrideMetadata extends OverrideMetadata {

@Nullable
private final Method method;
Expand Down

0 comments on commit 0ef5a40

Please sign in to comment.