Provide meta-annotation support in the TCF
Spring 3.0 already allows component stereotypes to be used in a meta-annotation fashion, for example by creating a custom @TransactionalService stereotype annotation which combines @Transactional and @Service in a single, reusable, application-specific annotation. However, the Spring TestContext Framework (TCF) currently does not provide any support for test-related annotations to be used as meta-annotations. This commit overhauls the TCF with regard to how annotations are retrieved and adds explicit support for the following annotations to be used as meta-annotations in conjunction with the TCF. - @ContextConfiguration - @ContextHierarchy - @ActiveProfiles - @DirtiesContext - @IfProfileValue - @ProfileValueSourceConfiguration - @BeforeTransaction - @AfterTransaction - @TransactionConfiguration - @Rollback - @TestExecutionListeners - @Repeat - @Timed - @WebAppConfiguration Note that meta-annotation support for @Transactional was already available prior to this commit. The following is a summary of the major changes included in this commit. - Now using AnnotationUtils.getAnnotation() instead of Class.getAnnotation() where appropriate in the TestContext Framework. - Now using AnnotationUtils.findAnnotation() instead of Class.isAnnotationPresent() where appropriate in the TestContext Framework. - Introduced findAnnotationPrefersInteracesOverLocalMetaAnnotations() in AnnotationUtilsTests in order to verify the status quo. - AnnotationUtils.findAnnotationDeclaringClass() and AnnotationUtils.findAnnotationDeclaringClassForTypes() now support meta annotations. - Introduced MetaAnnotationUtils and AnnotationDescriptor in the spring-test module. - Introduced UntypedAnnotationDescriptor in MetaAnnotationUtils. - Introduced findAnnotationDescriptorForTypes() in MetaAnnotationUtils. - ContextLoaderUtils now uses MetaAnnotationUtils for looking up @ActiveProfiles as a potential meta-annotation. - TestContextManager now uses MetaAnnotationUtils for looking up @TestExecutionListeners as a potential meta-annotation. - DirtiesContextTestExecutionListener now uses AnnotationUtils for looking up @DirtiesContext as a potential meta-annotation. - Introduced DirtiesContextTestExecutionListenerTests. - ProfileValueUtils now uses AnnotationUtils for looking up @IfProfileValue and @ProfileValueSourceConfiguration as potential meta-annotations. - @BeforeTransaction and @AfterTransaction now support ANNOTATION_TYPE as a target, allowing them to be used as meta-annotations. - TransactionalTestExecutionListener now uses AnnotationUtils for looking up @BeforeTransaction, @AfterTransaction, @Rollback, and @TransactionConfiguration as potential meta-annotations. - Introduced TransactionalTestExecutionListenerTests. - @Repeat and @Timed now support ANNOTATION_TYPE as a target, allowing them to be used as meta-annotations. - SpringJUnit4ClassRunner now uses AnnotationUtils for looking up @Repeat and @Timed as potential meta-annotations. - Moved all remaining logic for building the MergedContextConfiguration from the DefaultTestContext constructor to ContextLoaderUtils.buildMergedContextConfiguration(). - Verified meta-annotation support for @WebAppConfiguration and @ContextConfiguration. Issue: SPR-7827
This commit is contained in:
@@ -285,9 +285,8 @@ public abstract class AnnotationUtils {
|
||||
* <p>The standard {@link Class} API does not provide a mechanism for determining which class
|
||||
* in an inheritance hierarchy actually declares an {@link Annotation}, so we need to handle
|
||||
* this explicitly.
|
||||
* @param annotationType the Class object corresponding to the annotation type
|
||||
* @param clazz the Class object corresponding to the class on which to check for the annotation,
|
||||
* or {@code null}
|
||||
* @param annotationType the annotation class to look for, both locally and as a meta-annotation
|
||||
* @param clazz the class on which to check for the annotation, or {@code null}
|
||||
* @return the first {@link Class} in the inheritance hierarchy of the specified {@code clazz}
|
||||
* which declares an annotation for the specified {@code annotationType}, or {@code null}
|
||||
* if not found
|
||||
@@ -301,8 +300,24 @@ public abstract class AnnotationUtils {
|
||||
if (clazz == null || clazz.equals(Object.class)) {
|
||||
return null;
|
||||
}
|
||||
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz : findAnnotationDeclaringClass(
|
||||
annotationType, clazz.getSuperclass());
|
||||
|
||||
// Declared locally?
|
||||
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
// Declared on a stereotype annotation (i.e., as a meta-annotation)?
|
||||
if (!Annotation.class.isAssignableFrom(clazz)) {
|
||||
for (Annotation stereotype : clazz.getAnnotations()) {
|
||||
Class<?> declaringClass = findAnnotationDeclaringClass(annotationType, stereotype.annotationType());
|
||||
if (declaringClass != null) {
|
||||
return declaringClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Declared on a superclass?
|
||||
return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user