Browse Source

Add MergedAnnotation.getTypeHierarchy method

Add a `getTypeHierarchy()` method to `MergedAnnotation` that can be used
to return the full type hierarchy information. This method is
specifically designed to be used in combination with
`MergedAnnotationPredicates.unique`.

This update also allows us to delete the `parentAndType` method
from `AnnotatedElementUtils`.

Closes gh-22908
pull/25019/head
Phillip Webb 6 years ago committed by Juergen Hoeller
parent
commit
3b145a5a73
  1. 9
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  2. 19
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java
  3. 10
      spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java
  4. 6
      spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java
  5. 6
      spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java
  6. 9
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationTypeMappingsTests.java
  7. 9
      spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java
  8. 5
      spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java

9
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -502,7 +502,7 @@ public abstract class AnnotatedElementUtils { @@ -502,7 +502,7 @@ public abstract class AnnotatedElementUtils {
Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap);
return getAnnotations(element).stream(annotationName)
.filter(MergedAnnotationPredicates.unique(AnnotatedElementUtils::parentAndType))
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getTypeHierarchy))
.map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations));
}
@ -775,13 +775,6 @@ public abstract class AnnotatedElementUtils { @@ -775,13 +775,6 @@ public abstract class AnnotatedElementUtils {
repeatableContainers, AnnotationFilter.PLAIN);
}
private static Object parentAndType(MergedAnnotation<Annotation> annotation) {
if (annotation.getParent() == null) {
return annotation.getType().getName();
}
return annotation.getParent().getType().getName() + ":" + annotation.getParent().getType().getName();
}
@Nullable
private static MultiValueMap<String, Object> nullIfEmpty(MultiValueMap<String, Object> map) {
return (map.isEmpty() ? null : map);

19
spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java

@ -57,6 +57,8 @@ final class AnnotationTypeMapping { @@ -57,6 +57,8 @@ final class AnnotationTypeMapping {
private final Class<? extends Annotation> annotationType;
private final List<Class<? extends Annotation>> annotationTypeHierarchy;
@Nullable
private final Annotation annotation;
@ -84,6 +86,9 @@ final class AnnotationTypeMapping { @@ -84,6 +86,9 @@ final class AnnotationTypeMapping {
this.root = (parent != null ? parent.getRoot() : this);
this.depth = (parent == null ? 0 : parent.getDepth() + 1);
this.annotationType = annotationType;
this.annotationTypeHierarchy = merge(
parent != null ? parent.getAnnotationTypeHierarchy() : null,
annotationType);
this.annotation = annotation;
this.attributes = AttributeMethods.forAnnotationType(annotationType);
this.mirrorSets = new MirrorSets();
@ -98,6 +103,16 @@ final class AnnotationTypeMapping { @@ -98,6 +103,16 @@ final class AnnotationTypeMapping {
}
private static <T> List<T> merge(@Nullable List<T> existing, T element) {
if (existing == null) {
return Collections.singletonList(element);
}
List<T> merged = new ArrayList<>(existing.size() + 1);
merged.addAll(existing);
merged.add(element);
return Collections.unmodifiableList(merged);
}
private Map<Method, List<Method>> resolveAliasedForTargets() {
Map<Method, List<Method>> aliasedBy = new HashMap<>();
for (int i = 0; i < this.attributes.size(); i++) {
@ -372,6 +387,10 @@ final class AnnotationTypeMapping { @@ -372,6 +387,10 @@ final class AnnotationTypeMapping {
return this.annotationType;
}
List<Class<? extends Annotation>> getAnnotationTypeHierarchy() {
return this.annotationTypeHierarchy;
}
/**
* Get the source annotation for this mapping. This will be the
* meta-annotation, or {@code null} if this is the root mapping.

10
spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotation.java

@ -21,6 +21,7 @@ import java.lang.annotation.Inherited; @@ -21,6 +21,7 @@ import java.lang.annotation.Inherited;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Proxy;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
@ -72,6 +73,15 @@ public interface MergedAnnotation<A extends Annotation> { @@ -72,6 +73,15 @@ public interface MergedAnnotation<A extends Annotation> {
*/
Class<A> getType();
/**
* Return a complete type hierarchy from this annotation to the
* {@link #getRoot() root}. Provides a useful way to uniquely identify a
* merged annotation instance.
* @return the type heirarchy for the annotation
* @see MergedAnnotationPredicates#unique(Function)
*/
List<Class<? extends Annotation>> getTypeHierarchy();
/**
* Determine if the annotation is present on the source. Considers
* {@linkplain #isDirectlyPresent() directly present} and

6
spring-core/src/main/java/org/springframework/core/annotation/MissingMergedAnnotation.java

@ -18,6 +18,7 @@ package org.springframework.core.annotation; @@ -18,6 +18,7 @@ package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
@ -49,6 +50,11 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged @@ -49,6 +50,11 @@ final class MissingMergedAnnotation<A extends Annotation> extends AbstractMerged
throw new NoSuchElementException("Unable to get type for missing annotation");
}
@Override
public List<Class<? extends Annotation>> getTypeHierarchy() {
return Collections.emptyList();
}
@Override
public boolean isPresent() {
return false;

6
spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java

@ -21,6 +21,7 @@ import java.lang.reflect.Array; @@ -21,6 +21,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
@ -139,6 +140,11 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn @@ -139,6 +140,11 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
return (Class<A>) this.mapping.getAnnotationType();
}
@Override
public List<Class<? extends Annotation>> getTypeHierarchy() {
return this.mapping.getAnnotationTypeHierarchy();
}
@Override
public boolean isPresent() {
return true;

9
spring-core/src/test/java/org/springframework/core/annotation/AnnotationTypeMappingsTests.java

@ -245,6 +245,15 @@ public class AnnotationTypeMappingsTests { @@ -245,6 +245,15 @@ public class AnnotationTypeMappingsTests {
assertThat(mappings.get(1).getAnnotationType()).isEqualTo(MappedTarget.class);
}
@Test
public void getAnnotationTypeHierarchyReturnsTypeHierarchy() {
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
ThreeDeepA.class);
AnnotationTypeMapping mappingC = mappings.get(2);
assertThat(mappingC.getAnnotationTypeHierarchy()).containsExactly(
ThreeDeepA.class, ThreeDeepB.class, ThreeDeepC.class);
}
@Test
public void getAnnotationWhenRootReturnsNull() {
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(

9
spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java

@ -170,6 +170,15 @@ public class MergedAnnotationsTests { @@ -170,6 +170,15 @@ public class MergedAnnotationsTests {
assertThat(annotation.getRoot()).isSameAs(annotation);
}
@Test
public void getTypeHierarchy() {
MergedAnnotation<?> annotation = MergedAnnotations.from(
ComposedTransactionalComponentClass.class).get(
TransactionalComponent.class);
assertThat(annotation.getTypeHierarchy()).containsExactly(
ComposedTransactionalComponent.class, TransactionalComponent.class);
}
@Test
public void collectMultiValueMapFromNonAnnotatedClass() {
MultiValueMap<String, Object> map = MergedAnnotations.from(

5
spring-core/src/test/java/org/springframework/core/annotation/MissingMergedAnnotationTests.java

@ -44,6 +44,11 @@ public class MissingMergedAnnotationTests { @@ -44,6 +44,11 @@ public class MissingMergedAnnotationTests {
assertThatNoSuchElementException().isThrownBy(this.missing::getType);
}
@Test
public void getTypeHierarchyReturnsEmptyList() {
assertThat(this.missing.getTypeHierarchy()).isEmpty();
}
@Test
public void isPresentReturnsFalse() {
assertThat(this.missing.isPresent()).isFalse();

Loading…
Cancel
Save