|
|
|
|
@@ -45,8 +45,8 @@ import org.springframework.util.ObjectUtils;
|
|
|
|
|
* {@link org.springframework.test.context.TestExecutionListeners @TestExecutionListeners},
|
|
|
|
|
* and {@link org.springframework.test.context.ActiveProfiles @ActiveProfiles}
|
|
|
|
|
* which offer support for merging and overriding various <em>inherited</em>
|
|
|
|
|
* annotation attributes (e.g., {@link
|
|
|
|
|
* org.springframework.test.context.ContextConfiguration#inheritLocations}).
|
|
|
|
|
* annotation attributes (e.g.
|
|
|
|
|
* {@link org.springframework.test.context.ContextConfiguration#inheritLocations}).
|
|
|
|
|
*
|
|
|
|
|
* @author Sam Brannen
|
|
|
|
|
* @since 4.0
|
|
|
|
|
@@ -55,35 +55,26 @@ import org.springframework.util.ObjectUtils;
|
|
|
|
|
*/
|
|
|
|
|
public abstract class MetaAnnotationUtils {
|
|
|
|
|
|
|
|
|
|
private MetaAnnotationUtils() {
|
|
|
|
|
/* no-op */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find the {@link AnnotationDescriptor} for the supplied {@code annotationType}
|
|
|
|
|
* on the supplied {@link Class}, traversing its annotations and superclasses
|
|
|
|
|
* if no annotation can be found on the given class itself.
|
|
|
|
|
*
|
|
|
|
|
* <p>This method explicitly handles class-level annotations which are not
|
|
|
|
|
* declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as
|
|
|
|
|
* well as meta-annotations</em>.
|
|
|
|
|
*
|
|
|
|
|
* <p>The algorithm operates as follows:
|
|
|
|
|
* <ol>
|
|
|
|
|
* <li>Search for the annotation on the given class and return a corresponding
|
|
|
|
|
* {@code AnnotationDescriptor} if found.
|
|
|
|
|
* <li>Recursively search through all annotations that the given class declares.
|
|
|
|
|
* <li>Recursively search through the superclass hierarchy of the given class.
|
|
|
|
|
* <li>Search for the annotation on the given class and return a corresponding
|
|
|
|
|
* {@code AnnotationDescriptor} if found.
|
|
|
|
|
* <li>Recursively search through all annotations that the given class declares.
|
|
|
|
|
* <li>Recursively search through the superclass hierarchy of the given class.
|
|
|
|
|
* </ol>
|
|
|
|
|
*
|
|
|
|
|
* <p>In this context, the term <em>recursively</em> means that the search
|
|
|
|
|
* process continues by returning to step #1 with the current annotation or
|
|
|
|
|
* superclass as the class to look for annotations on.
|
|
|
|
|
*
|
|
|
|
|
* <p>If the supplied {@code clazz} is an interface, only the interface
|
|
|
|
|
* itself will be checked; the inheritance hierarchy for interfaces will not
|
|
|
|
|
* be traversed.
|
|
|
|
|
*
|
|
|
|
|
* @param clazz the class to look for annotations on
|
|
|
|
|
* @param annotationType the type of annotation to look for
|
|
|
|
|
* @return the corresponding annotation descriptor if the annotation was found;
|
|
|
|
|
@@ -91,8 +82,9 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
* @see AnnotationUtils#findAnnotationDeclaringClass(Class, Class)
|
|
|
|
|
* @see #findAnnotationDescriptorForTypes(Class, Class...)
|
|
|
|
|
*/
|
|
|
|
|
public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor(Class<?> clazz,
|
|
|
|
|
Class<T> annotationType) {
|
|
|
|
|
public static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor(
|
|
|
|
|
Class<?> clazz, Class<T> annotationType) {
|
|
|
|
|
|
|
|
|
|
return findAnnotationDescriptor(clazz, new HashSet<Annotation>(), annotationType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -100,7 +92,6 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
* Perform the search algorithm for {@link #findAnnotationDescriptor(Class, Class)},
|
|
|
|
|
* avoiding endless recursion by tracking which annotations have already been
|
|
|
|
|
* <em>visited</em>.
|
|
|
|
|
*
|
|
|
|
|
* @param clazz the class to look for annotations on
|
|
|
|
|
* @param visited the set of annotations that have already been visited
|
|
|
|
|
* @param annotationType the type of annotation to look for
|
|
|
|
|
@@ -142,31 +133,25 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
* in the inheritance hierarchy of the specified {@code clazz} (including
|
|
|
|
|
* the specified {@code clazz} itself) which declares at least one of the
|
|
|
|
|
* specified {@code annotationTypes}.
|
|
|
|
|
*
|
|
|
|
|
* <p>This method traverses the annotations and superclasses of the specified
|
|
|
|
|
* {@code clazz} if no annotation can be found on the given class itself.
|
|
|
|
|
*
|
|
|
|
|
* <p>This method explicitly handles class-level annotations which are not
|
|
|
|
|
* declared as {@linkplain java.lang.annotation.Inherited inherited} <em>as
|
|
|
|
|
* well as meta-annotations</em>.
|
|
|
|
|
*
|
|
|
|
|
* <p>The algorithm operates as follows:
|
|
|
|
|
* <ol>
|
|
|
|
|
* <li>Search for a local declaration of one of the annotation types on
|
|
|
|
|
* the given class and return a corresponding {@code UntypedAnnotationDescriptor}
|
|
|
|
|
* if found.
|
|
|
|
|
* <li>Recursively search through all annotations that the given class declares.
|
|
|
|
|
* <li>Recursively search through the superclass hierarchy of the given class.
|
|
|
|
|
* <li>Search for a local declaration of one of the annotation types on
|
|
|
|
|
* the given class and return a corresponding {@code UntypedAnnotationDescriptor}
|
|
|
|
|
* if found.
|
|
|
|
|
* <li>Recursively search through all annotations that the given class declares.
|
|
|
|
|
* <li>Recursively search through the superclass hierarchy of the given class.
|
|
|
|
|
* </ol>
|
|
|
|
|
*
|
|
|
|
|
* <p>In this context, the term <em>recursively</em> means that the search
|
|
|
|
|
* process continues by returning to step #1 with the current annotation or
|
|
|
|
|
* superclass as the class to look for annotations on.
|
|
|
|
|
*
|
|
|
|
|
* <p>If the supplied {@code clazz} is an interface, only the interface
|
|
|
|
|
* itself will be checked; the inheritance hierarchy for interfaces will not
|
|
|
|
|
* be traversed.
|
|
|
|
|
*
|
|
|
|
|
* @param clazz the class to look for annotations on
|
|
|
|
|
* @param annotationTypes the types of annotations to look for
|
|
|
|
|
* @return the corresponding annotation descriptor if one of the annotations
|
|
|
|
|
@@ -175,8 +160,9 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
* @see #findAnnotationDescriptor(Class, Class)
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Class<?> clazz,
|
|
|
|
|
Class<? extends Annotation>... annotationTypes) {
|
|
|
|
|
public static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(
|
|
|
|
|
Class<?> clazz, Class<? extends Annotation>... annotationTypes) {
|
|
|
|
|
|
|
|
|
|
return findAnnotationDescriptorForTypes(clazz, new HashSet<Annotation>(), annotationTypes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -184,7 +170,6 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
* Perform the search algorithm for {@link #findAnnotationDescriptorForTypes(Class, Class...)},
|
|
|
|
|
* avoiding endless recursion by tracking which annotations have already been
|
|
|
|
|
* <em>visited</em>.
|
|
|
|
|
*
|
|
|
|
|
* @param clazz the class to look for annotations on
|
|
|
|
|
* @param visited the set of annotations that have already been visited
|
|
|
|
|
* @param annotationTypes the types of annotations to look for
|
|
|
|
|
@@ -196,7 +181,6 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
Set<Annotation> visited, Class<? extends Annotation>... annotationTypes) {
|
|
|
|
|
|
|
|
|
|
assertNonEmptyAnnotationTypeArray(annotationTypes, "The list of annotation types must not be empty");
|
|
|
|
|
|
|
|
|
|
if (clazz == null || clazz.equals(Object.class)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -224,49 +208,50 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
return findAnnotationDescriptorForTypes(clazz.getSuperclass(), visited, annotationTypes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void assertNonEmptyAnnotationTypeArray(Class<?>[] annotationTypes, String message) {
|
|
|
|
|
if (ObjectUtils.isEmpty(annotationTypes)) {
|
|
|
|
|
throw new IllegalArgumentException(message);
|
|
|
|
|
}
|
|
|
|
|
for (Class<?> clazz : annotationTypes) {
|
|
|
|
|
if (!Annotation.class.isAssignableFrom(clazz)) {
|
|
|
|
|
throw new IllegalArgumentException("Array elements must be of type Annotation");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Descriptor for an {@link Annotation}, including the {@linkplain
|
|
|
|
|
* #getDeclaringClass() class} on which the annotation is <em>declared</em>
|
|
|
|
|
* as well as the actual {@linkplain #getAnnotation() annotation} instance.
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* If the annotation is used as a meta-annotation, the descriptor also includes
|
|
|
|
|
* <p>If the annotation is used as a meta-annotation, the descriptor also includes
|
|
|
|
|
* the {@linkplain #getComposedAnnotation() composed annotation} on which the
|
|
|
|
|
* annotation is present. In such cases, the <em>root declaring class</em> is
|
|
|
|
|
* not directly annotated with the annotation but rather indirectly via the
|
|
|
|
|
* composed annotation.
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* Given the following example, if we are searching for the {@code @Transactional}
|
|
|
|
|
* <p>Given the following example, if we are searching for the {@code @Transactional}
|
|
|
|
|
* annotation <em>on</em> the {@code TransactionalTests} class, then the
|
|
|
|
|
* properties of the {@code AnnotationDescriptor} would be as follows.
|
|
|
|
|
*
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>rootDeclaringClass: {@code TransactionalTests} class object</li>
|
|
|
|
|
* <li>declaringClass: {@code TransactionalTests} class object</li>
|
|
|
|
|
* <li>composedAnnotation: {@code null}</li>
|
|
|
|
|
* <li>annotation: instance of the {@code Transactional} annotation</li>
|
|
|
|
|
* <li>rootDeclaringClass: {@code TransactionalTests} class object</li>
|
|
|
|
|
* <li>declaringClass: {@code TransactionalTests} class object</li>
|
|
|
|
|
* <li>composedAnnotation: {@code null}</li>
|
|
|
|
|
* <li>annotation: instance of the {@code Transactional} annotation</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
*
|
|
|
|
|
* <pre style="code">
|
|
|
|
|
* @Transactional
|
|
|
|
|
* @ContextConfiguration({"/test-datasource.xml", "/repository-config.xml"})
|
|
|
|
|
* public class TransactionalTests { }
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* Given the following example, if we are searching for the {@code @Transactional}
|
|
|
|
|
* <p>Given the following example, if we are searching for the {@code @Transactional}
|
|
|
|
|
* annotation <em>on</em> the {@code UserRepositoryTests} class, then the
|
|
|
|
|
* properties of the {@code AnnotationDescriptor} would be as follows.
|
|
|
|
|
*
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>rootDeclaringClass: {@code UserRepositoryTests} class object</li>
|
|
|
|
|
* <li>declaringClass: {@code RepositoryTests} class object</li>
|
|
|
|
|
* <li>composedAnnotation: instance of the {@code RepositoryTests} annotation</li>
|
|
|
|
|
* <li>annotation: instance of the {@code Transactional} annotation</li>
|
|
|
|
|
* <li>rootDeclaringClass: {@code UserRepositoryTests} class object</li>
|
|
|
|
|
* <li>declaringClass: {@code RepositoryTests} class object</li>
|
|
|
|
|
* <li>composedAnnotation: instance of the {@code RepositoryTests} annotation</li>
|
|
|
|
|
* <li>annotation: instance of the {@code Transactional} annotation</li>
|
|
|
|
|
* </ul>
|
|
|
|
|
*
|
|
|
|
|
* <pre style="code">
|
|
|
|
|
* @Transactional
|
|
|
|
|
* @ContextConfiguration({"/test-datasource.xml", "/repository-config.xml"})
|
|
|
|
|
@@ -276,18 +261,18 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
* @RepositoryTests
|
|
|
|
|
* public class UserRepositoryTests { }
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* @author Sam Brannen
|
|
|
|
|
* @since 4.0
|
|
|
|
|
*/
|
|
|
|
|
public static class AnnotationDescriptor<T extends Annotation> {
|
|
|
|
|
|
|
|
|
|
private final Class<?> rootDeclaringClass;
|
|
|
|
|
private final Class<?> declaringClass;
|
|
|
|
|
private final Annotation composedAnnotation;
|
|
|
|
|
private final T annotation;
|
|
|
|
|
private final AnnotationAttributes annotationAttributes;
|
|
|
|
|
|
|
|
|
|
private final Class<?> declaringClass;
|
|
|
|
|
|
|
|
|
|
private final Annotation composedAnnotation;
|
|
|
|
|
|
|
|
|
|
private final T annotation;
|
|
|
|
|
|
|
|
|
|
private final AnnotationAttributes annotationAttributes;
|
|
|
|
|
|
|
|
|
|
public AnnotationDescriptor(Class<?> rootDeclaringClass, T annotation) {
|
|
|
|
|
this(rootDeclaringClass, rootDeclaringClass, null, annotation);
|
|
|
|
|
@@ -297,13 +282,12 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
Annotation composedAnnotation, T annotation) {
|
|
|
|
|
Assert.notNull(rootDeclaringClass, "rootDeclaringClass must not be null");
|
|
|
|
|
Assert.notNull(annotation, "annotation must not be null");
|
|
|
|
|
|
|
|
|
|
this.rootDeclaringClass = rootDeclaringClass;
|
|
|
|
|
this.declaringClass = declaringClass;
|
|
|
|
|
this.composedAnnotation = composedAnnotation;
|
|
|
|
|
this.annotation = annotation;
|
|
|
|
|
this.annotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(rootDeclaringClass,
|
|
|
|
|
annotation.annotationType().getName());
|
|
|
|
|
this.annotationAttributes = AnnotatedElementUtils.getAnnotationAttributes(
|
|
|
|
|
rootDeclaringClass, annotation.annotationType().getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Class<?> getRootDeclaringClass() {
|
|
|
|
|
@@ -331,7 +315,7 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Class<? extends Annotation> getComposedAnnotationType() {
|
|
|
|
|
return this.composedAnnotation == null ? null : this.composedAnnotation.annotationType();
|
|
|
|
|
return (this.composedAnnotation != null ? this.composedAnnotation.annotationType() : null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -339,22 +323,20 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
return new ToStringCreator(this)//
|
|
|
|
|
.append("rootDeclaringClass", rootDeclaringClass)//
|
|
|
|
|
.append("declaringClass", declaringClass)//
|
|
|
|
|
.append("composedAnnotation", composedAnnotation)//
|
|
|
|
|
.append("annotation", annotation)//
|
|
|
|
|
.toString();
|
|
|
|
|
return new ToStringCreator(this)
|
|
|
|
|
.append("rootDeclaringClass", this.rootDeclaringClass)
|
|
|
|
|
.append("declaringClass", this.declaringClass)
|
|
|
|
|
.append("composedAnnotation", this.composedAnnotation)
|
|
|
|
|
.append("annotation", this.annotation)
|
|
|
|
|
.toString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <em>Untyped</em> extension of {@code AnnotationDescriptor} that is used
|
|
|
|
|
* to describe the declaration of one of several candidate annotation types
|
|
|
|
|
* where the actual annotation type cannot be predetermined.
|
|
|
|
|
*
|
|
|
|
|
* @author Sam Brannen
|
|
|
|
|
* @since 4.0
|
|
|
|
|
*/
|
|
|
|
|
public static class UntypedAnnotationDescriptor extends AnnotationDescriptor<Annotation> {
|
|
|
|
|
|
|
|
|
|
@@ -368,17 +350,4 @@ public abstract class MetaAnnotationUtils {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void assertNonEmptyAnnotationTypeArray(Class<?>[] annotationTypes, String message) {
|
|
|
|
|
if (ObjectUtils.isEmpty(annotationTypes)) {
|
|
|
|
|
throw new IllegalArgumentException(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Class<?> clazz : annotationTypes) {
|
|
|
|
|
if (!Annotation.class.isAssignableFrom(clazz)) {
|
|
|
|
|
throw new IllegalArgumentException("Array elements must be of type Annotation");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|