Refine MergedAnnotation.asMap

Add a convenience method that allows a `MergedAnnotation` to be
converted into an `AnnotationAttributes` instance. Also rename
the `MapValues` enum to `Adapt` which generally seems to read
better.

Closes gh-22738
This commit is contained in:
Phillip Webb 2019-04-05 11:10:17 -07:00
parent ef151f578d
commit e905f1712e
10 changed files with 105 additions and 68 deletions

View File

@ -166,6 +166,11 @@ abstract class AbstractMergedAnnotation<A extends Annotation> implements MergedA
return filterAttributes(this::hasNonDefaultValue); return filterAttributes(this::hasNonDefaultValue);
} }
@Override
public AnnotationAttributes asAnnotationAttributes(Adapt... adaptations) {
return asMap(mergedAnnotation -> new AnnotationAttributes(getType()), adaptations);
}
@Override @Override
public Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition) public Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition)
throws NoSuchElementException { throws NoSuchElementException {

View File

@ -25,7 +25,7 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.core.BridgeMethodResolver; import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.MergedAnnotation.MapValues; import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -500,11 +500,11 @@ public abstract class AnnotatedElementUtils {
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap); Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
return getAnnotations(element).stream(annotationName) return getAnnotations(element).stream(annotationName)
.filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType)) .filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType))
.map(MergedAnnotation::withNonMergedAttributes) .map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, mapValues)); .collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations));
} }
/** /**
@ -799,8 +799,8 @@ public abstract class AnnotatedElementUtils {
if (!annotation.isPresent()) { if (!annotation.isPresent()) {
return null; return null;
} }
return annotation.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()), return annotation.asAnnotationAttributes(
MapValues.of(classValuesAsString, nestedAnnotationsAsMap)); Adapt.values(classValuesAsString, nestedAnnotationsAsMap));
} }

View File

@ -29,11 +29,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import org.springframework.core.BridgeMethodResolver; import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet; import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
import org.springframework.core.annotation.MergedAnnotation.MapValues; import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ConcurrentReferenceHashMap;
@ -857,14 +856,11 @@ public abstract class AnnotationUtils {
@Nullable AnnotatedElement annotatedElement, Annotation annotation, @Nullable AnnotatedElement annotatedElement, Annotation annotation,
boolean classValuesAsString, boolean nestedAnnotationsAsMap) { boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
MapValues[] mapValues = MapValues.of(classValuesAsString, nestedAnnotationsAsMap); Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
return MergedAnnotation.from(annotatedElement, annotation) return MergedAnnotation.from(annotatedElement, annotation)
.withNonMergedAttributes() .withNonMergedAttributes()
.asMap(getAnnotationAttributesFactory(), mapValues); .asMap(mergedAnnotation ->
} new AnnotationAttributes(mergedAnnotation.getType(), true), adaptations);
private static Function<MergedAnnotation<?>, AnnotationAttributes> getAnnotationAttributesFactory() {
return (annotation -> new AnnotationAttributes(annotation.getType(), true));
} }
/** /**
@ -910,7 +906,8 @@ public abstract class AnnotationUtils {
else { else {
// If we have nested annotations, we need them as nested maps // If we have nested annotations, we need them as nested maps
AnnotationAttributes attributes = MergedAnnotation.from(annotationType) AnnotationAttributes attributes = MergedAnnotation.from(annotationType)
.asMap(getAnnotationAttributesFactory(), MapValues.ANNOTATION_TO_MAP); .asMap(annotation ->
new AnnotationAttributes(annotation.getType(), true), Adapt.ANNOTATION_TO_MAP);
for (Map.Entry<String, Object> element : attributes.entrySet()) { for (Map.Entry<String, Object> element : attributes.entrySet()) {
result.put(element.getKey(), new DefaultValueHolder(element.getValue())); result.put(element.getKey(), new DefaultValueHolder(element.getValue()));
} }

View File

@ -422,21 +422,32 @@ public interface MergedAnnotation<A extends Annotation> {
MergedAnnotation<A> withNonMergedAttributes(); MergedAnnotation<A> withNonMergedAttributes();
/** /**
* Create an immutable {@link Map} that contains all the annotation attributes. * Create a new mutable {@link AnnotationAttributes} instance from this
* <p>The {@link MapValues options} may be used to change the way that values are added. * merged annotation.
* @param options map value options * <p>The {@link Adapt adaptations} may be used to change the way that values
* are added.
* @param adaptations adaptations that should be applied to the annotation values
* @return an immutable map containing the attributes and values * @return an immutable map containing the attributes and values
*/ */
Map<String, Object> asMap(MapValues... options); AnnotationAttributes asAnnotationAttributes(Adapt... adaptations);
/** /**
* Create a {@link Map} of the given type that contains all the annotation attributes. * Return an immutable {@link Map} that contains all the annotation attributes.
* <p>The {@link MapValues options} may be used to change the way that values are added. * <p>The {@link Adapt adaptations} may be used to change the way that values are added.
* @param adaptations adaptations that should be applied to the annotation values
* @return an immutable map containing the attributes and values
*/
Map<String, Object> asMap(Adapt... adaptations);
/**
* Create a new {@link Map} instance of the given type that contains all the annotation
* attributes.
* <p>The {@link Adapt adaptations} may be used to change the way that values are added.
* @param factory a map factory * @param factory a map factory
* @param options map value options * @param adaptations adaptations that should be applied to the annotation values
* @return a map containing the attributes and values * @return a map containing the attributes and values
*/ */
<T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options); <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations);
/** /**
* Create a type-safe synthesized version of this annotation that can be * Create a type-safe synthesized version of this annotation that can be
@ -539,24 +550,25 @@ public interface MergedAnnotation<A extends Annotation> {
/** /**
* Options that effect the way map values are * Adaptations that can be applied to attributes values when creating
* {@linkplain MergedAnnotation#asMap(MapValues...) converted}. * {@linkplain MergedAnnotation#asMap(Adapt...) Maps} or
* {@link MergedAnnotation#asAnnotationAttributes(Adapt...) AnnotationAttributes}.
*/ */
enum MapValues { enum Adapt {
/** /**
* Add class or class array attributes as strings. * Adapt class or class array attributes to strings.
*/ */
CLASS_TO_STRING, CLASS_TO_STRING,
/** /**
* Convert any nested annotation or annotation arrays to maps rather * Adapt nested annotation or annotation arrays to maps rather
* than synthesizing the values. * than synthesizing the values.
*/ */
ANNOTATION_TO_MAP; ANNOTATION_TO_MAP;
protected final boolean isIn(MapValues... options) { protected final boolean isIn(Adapt... adaptations) {
for (MapValues candidate : options) { for (Adapt candidate : adaptations) {
if (candidate == this) { if (candidate == this) {
return true; return true;
} }
@ -565,16 +577,16 @@ public interface MergedAnnotation<A extends Annotation> {
} }
/** /**
* Factory method to create a {@link MapValues} array from a set of boolean flags. * Factory method to create a {@link Adapt} array from a set of boolean flags.
* @param classToString if {@link MapValues#CLASS_TO_STRING} is included * @param classToString if {@link Adapt#CLASS_TO_STRING} is included
* @param annotationsToMap if {@link MapValues#ANNOTATION_TO_MAP} is included * @param annotationsToMap if {@link Adapt#ANNOTATION_TO_MAP} is included
* @return a new {@link MapValues} array * @return a new {@link Adapt} array
*/ */
public static MapValues[] of(boolean classToString, boolean annotationsToMap) { public static Adapt[] values(boolean classToString, boolean annotationsToMap) {
EnumSet<MapValues> result = EnumSet.noneOf(MapValues.class); EnumSet<Adapt> result = EnumSet.noneOf(Adapt.class);
addIfTrue(result, MapValues.CLASS_TO_STRING, classToString); addIfTrue(result, Adapt.CLASS_TO_STRING, classToString);
addIfTrue(result, MapValues.ANNOTATION_TO_MAP, annotationsToMap); addIfTrue(result, Adapt.ANNOTATION_TO_MAP, annotationsToMap);
return result.toArray(new MapValues[0]); return result.toArray(new Adapt[0]);
} }
private static <T> void addIfTrue(Set<T> result, T value, boolean test) { private static <T> void addIfTrue(Set<T> result, T value, boolean test) {

View File

@ -26,7 +26,7 @@ import java.util.function.IntFunction;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics; import java.util.stream.Collector.Characteristics;
import org.springframework.core.annotation.MergedAnnotation.MapValues; import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -97,39 +97,39 @@ public abstract class MergedAnnotationCollectors {
* Create a new {@link Collector} that accumulates merged annotations to an * Create a new {@link Collector} that accumulates merged annotations to an
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object) * {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
* added} from each merged annotation * added} from each merged annotation
* {@link MergedAnnotation#asMap(MapValues...) as a map}. * {@link MergedAnnotation#asMap(Adapt...) as a map}.
* @param <A> the annotation type * @param <A> the annotation type
* @param options the map conversion options * @param adaptations adaptations that should be applied to the annotation values
* @return a {@link Collector} which collects and synthesizes the * @return a {@link Collector} which collects and synthesizes the
* annotations into a {@link LinkedMultiValueMap} * annotations into a {@link LinkedMultiValueMap}
* @see #toMultiValueMap(Function, MergedAnnotation.MapValues...) * @see #toMultiValueMap(Function, MergedAnnotation.Adapt...)
*/ */
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap( public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap(
MapValues... options) { Adapt... adaptations) {
return toMultiValueMap(Function.identity(), options); return toMultiValueMap(Function.identity(), adaptations);
} }
/** /**
* Create a new {@link Collector} that accumulates merged annotations to an * Create a new {@link Collector} that accumulates merged annotations to an
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object) * {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
* added} from each merged annotation * added} from each merged annotation
* {@link MergedAnnotation#asMap(MapValues...) as a map}. * {@link MergedAnnotation#asMap(Adapt...) as a map}.
* @param <A> the annotation type * @param <A> the annotation type
* @param options the map conversion options * @param adaptations adaptations that should be applied to the annotation values
* @param finisher the finisher function for the new {@link MultiValueMap} * @param finisher the finisher function for the new {@link MultiValueMap}
* @return a {@link Collector} which collects and synthesizes the * @return a {@link Collector} which collects and synthesizes the
* annotations into a {@link LinkedMultiValueMap} * annotations into a {@link LinkedMultiValueMap}
* @see #toMultiValueMap(MergedAnnotation.MapValues...) * @see #toMultiValueMap(MergedAnnotation.Adapt...)
*/ */
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap( public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap(
Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher, Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher,
MapValues... options) { Adapt... adaptations) {
Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ? Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ?
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS); IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
return Collector.of(LinkedMultiValueMap::new, return Collector.of(LinkedMultiValueMap::new,
(map, annotation) -> annotation.asMap(options).forEach(map::add), (map, annotation) -> annotation.asMap(adaptations).forEach(map::add),
MergedAnnotationCollectors::merge, finisher, characteristics); MergedAnnotationCollectors::merge, finisher, characteristics);
} }

View File

@ -108,12 +108,17 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
} }
@Override @Override
public Map<String, Object> asMap(MapValues... options) { public AnnotationAttributes asAnnotationAttributes(Adapt... adaptations) {
return new AnnotationAttributes();
}
@Override
public Map<String, Object> asMap(Adapt... adaptations) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
@Override @Override
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options) { public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations) {
return factory.apply(this); return factory.apply(this);
} }

View File

@ -233,50 +233,50 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
} }
@Override @Override
public Map<String, Object> asMap(MapValues... options) { public Map<String, Object> asMap(Adapt... adaptations) {
return Collections.unmodifiableMap(asMap(mergedAnnotation -> new LinkedHashMap<>(), options)); return Collections.unmodifiableMap(asMap(mergedAnnotation -> new LinkedHashMap<>(), adaptations));
} }
@Override @Override
public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, MapValues... options) { public <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations) {
T map = factory.apply(this); T map = factory.apply(this);
Assert.state(map != null, "Factory used to create MergedAnnotation Map must not return null"); Assert.state(map != null, "Factory used to create MergedAnnotation Map must not return null");
AttributeMethods attributes = this.mapping.getAttributes(); AttributeMethods attributes = this.mapping.getAttributes();
for (int i = 0; i < attributes.size(); i++) { for (int i = 0; i < attributes.size(); i++) {
Method attribute = attributes.get(i); Method attribute = attributes.get(i);
Object value = (isFiltered(attribute.getName()) ? null : Object value = (isFiltered(attribute.getName()) ? null :
getValue(i, getTypeForMapOptions(attribute, options))); getValue(i, getTypeForMapOptions(attribute, adaptations)));
if (value != null) { if (value != null) {
map.put(attribute.getName(), map.put(attribute.getName(),
adaptValueForMapOptions(attribute, value, map.getClass(), factory, options)); adaptValueForMapOptions(attribute, value, map.getClass(), factory, adaptations));
} }
} }
return map; return map;
} }
private Class<?> getTypeForMapOptions(Method attribute, MapValues[] options) { private Class<?> getTypeForMapOptions(Method attribute, Adapt[] adaptations) {
Class<?> attributeType = attribute.getReturnType(); Class<?> attributeType = attribute.getReturnType();
Class<?> componentType = (attributeType.isArray() ? attributeType.getComponentType() : attributeType); Class<?> componentType = (attributeType.isArray() ? attributeType.getComponentType() : attributeType);
if (MapValues.CLASS_TO_STRING.isIn(options) && componentType == Class.class) { if (Adapt.CLASS_TO_STRING.isIn(adaptations) && componentType == Class.class) {
return (attributeType.isArray() ? String[].class : String.class); return (attributeType.isArray() ? String[].class : String.class);
} }
return Object.class; return Object.class;
} }
private <T extends Map<String, Object>> Object adaptValueForMapOptions(Method attribute, Object value, private <T extends Map<String, Object>> Object adaptValueForMapOptions(Method attribute, Object value,
Class<?> mapType, Function<MergedAnnotation<?>, T> factory, MapValues[] options) { Class<?> mapType, Function<MergedAnnotation<?>, T> factory, Adapt[] adaptations) {
if (value instanceof MergedAnnotation) { if (value instanceof MergedAnnotation) {
MergedAnnotation<?> annotation = (MergedAnnotation<?>) value; MergedAnnotation<?> annotation = (MergedAnnotation<?>) value;
return (MapValues.ANNOTATION_TO_MAP.isIn(options) ? return (Adapt.ANNOTATION_TO_MAP.isIn(adaptations) ?
annotation.asMap(factory, options) : annotation.synthesize()); annotation.asMap(factory, adaptations) : annotation.synthesize());
} }
if (value instanceof MergedAnnotation[]) { if (value instanceof MergedAnnotation[]) {
MergedAnnotation<?>[] annotations = (MergedAnnotation<?>[]) value; MergedAnnotation<?>[] annotations = (MergedAnnotation<?>[]) value;
if (MapValues.ANNOTATION_TO_MAP.isIn(options)) { if (Adapt.ANNOTATION_TO_MAP.isIn(adaptations)) {
Object result = Array.newInstance(mapType, annotations.length); Object result = Array.newInstance(mapType, annotations.length);
for (int i = 0; i < annotations.length; i++) { for (int i = 0; i < annotations.length; i++) {
Array.set(result, i, annotations[i].asMap(factory, options)); Array.set(result, i, annotations[i].asMap(factory, adaptations));
} }
return result; return result;
} }

View File

@ -27,7 +27,7 @@ import java.util.stream.Stream;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.annotation.MergedAnnotation.MapValues; import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
@ -72,7 +72,7 @@ public class MergedAnnotationCollectorsTests {
MultiValueMap<String, Object> map = stream().map( MultiValueMap<String, Object> map = stream().map(
MergedAnnotation::filterDefaultValues).collect( MergedAnnotation::filterDefaultValues).collect(
MergedAnnotationCollectors.toMultiValueMap( MergedAnnotationCollectors.toMultiValueMap(
MapValues.CLASS_TO_STRING)); Adapt.CLASS_TO_STRING));
assertThat(map.get("value")).containsExactly("a", "b", "c"); assertThat(map.get("value")).containsExactly("a", "b", "c");
assertThat(map.get("extra")).containsExactly("java.lang.String", assertThat(map.get("extra")).containsExactly("java.lang.String",
"java.lang.Integer"); "java.lang.Integer");

View File

@ -40,7 +40,7 @@ import org.junit.Test;
import org.junit.internal.ArrayComparisonFailure; import org.junit.internal.ArrayComparisonFailure;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.MergedAnnotation.MapValues; import org.springframework.core.annotation.MergedAnnotation.Adapt;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass; import org.springframework.core.annotation.subpackage.NonPublicAnnotatedClass;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -1665,7 +1665,7 @@ public class MergedAnnotationsTests {
assertThat(componentScan.value().pattern()).isEqualTo("*Foo"); assertThat(componentScan.value().pattern()).isEqualTo("*Foo");
Map<String, Object> map = MergedAnnotation.from(componentScan).asMap( Map<String, Object> map = MergedAnnotation.from(componentScan).asMap(
annotation -> new LinkedHashMap<String, Object>(), annotation -> new LinkedHashMap<String, Object>(),
MapValues.ANNOTATION_TO_MAP); Adapt.ANNOTATION_TO_MAP);
Map<String, Object> filterMap = (Map<String, Object>) map.get("value"); Map<String, Object> filterMap = (Map<String, Object>) map.get("value");
assertThat(filterMap.get("pattern")).isEqualTo("*Foo"); assertThat(filterMap.get("pattern")).isEqualTo("*Foo");
filterMap.put("pattern", "newFoo"); filterMap.put("pattern", "newFoo");
@ -1685,7 +1685,7 @@ public class MergedAnnotationsTests {
assertThat(componentScan).isNotNull(); assertThat(componentScan).isNotNull();
Map<String, Object> map = MergedAnnotation.from(componentScan).asMap( Map<String, Object> map = MergedAnnotation.from(componentScan).asMap(
annotation -> new LinkedHashMap<String, Object>(), annotation -> new LinkedHashMap<String, Object>(),
MapValues.ANNOTATION_TO_MAP); Adapt.ANNOTATION_TO_MAP);
Map<String, Object>[] filters = (Map[]) map.get("excludeFilters"); Map<String, Object>[] filters = (Map[]) map.get("excludeFilters");
List<String> patterns = Arrays.stream(filters).map( List<String> patterns = Arrays.stream(filters).map(
m -> (String) m.get("pattern")).collect(Collectors.toList()); m -> (String) m.get("pattern")).collect(Collectors.toList());
@ -2053,6 +2053,17 @@ public class MergedAnnotationsTests {
"FromValueAttributeMeta"); "FromValueAttributeMeta");
} }
@Test
public void asAnnotationAttributesReturnsPopulatedAnnotationAttributes() {
MergedAnnotation<?> annotation = MergedAnnotations.from(
SpringApplicationConfigurationClass.class).get(
SpringApplicationConfiguration.class);
AnnotationAttributes attributes = annotation.asAnnotationAttributes(
Adapt.CLASS_TO_STRING);
assertThat(attributes).containsEntry("classes", new String[] { Number.class.getName() });
assertThat(attributes.annotationType()).isEqualTo(SpringApplicationConfiguration.class);
}
// @formatter:off // @formatter:off
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

View File

@ -264,6 +264,13 @@ public class MissingMergedAnnotationTests {
assertThat(this.missing.toString()).isEqualTo("(missing)"); assertThat(this.missing.toString()).isEqualTo("(missing)");
} }
@Test
public void asAnnotationAttributesReturnsNewAnnotationAttributes() {
AnnotationAttributes attributes = this.missing.asAnnotationAttributes();
assertThat(attributes).isEmpty();
assertThat(this.missing.asAnnotationAttributes()).isNotSameAs(attributes);
}
@Test @Test
public void asMapReturnsEmptyMap() { public void asMapReturnsEmptyMap() {
Map<String, Object> map = this.missing.asMap(); Map<String, Object> map = this.missing.asMap();