Provide support for context hierarchies in the TCF
Prior to this commit the Spring TestContext Framework supported creating only flat, non-hierarchical contexts. There was no easy way to create contexts with parent-child relationships. This commit addresses this issue by introducing a new @ContextHierarchy annotation that can be used in conjunction with @ContextConfiguration for declaring hierarchies of application contexts, either within a single test class or within a test class hierarchy. In addition, @DirtiesContext now supports a new 'hierarchyMode' attribute for controlling context cache clearing for context hierarchies. - Introduced a new @ContextHierarchy annotation. - Introduced 'name' attribute in @ContextConfiguration. - Introduced 'name' property in ContextConfigurationAttributes. - TestContext is now aware of @ContextHierarchy in addition to @ContextConfiguration. - Introduced findAnnotationDeclaringClassForTypes() in AnnotationUtils. - Introduced resolveContextHierarchyAttributes() in ContextLoaderUtils. - Introduced buildContextHierarchyMap() in ContextLoaderUtils. - @ContextConfiguration and @ContextHierarchy may not be used as top-level, class-level annotations simultaneously. - Introduced reference to the parent configuration in MergedContextConfiguration and WebMergedContextConfiguration. - Introduced overloaded buildMergedContextConfiguration() methods in ContextLoaderUtils in order to handle context hierarchies separately from conventional, non-hierarchical contexts. - Introduced hashCode() and equals() in ContextConfigurationAttributes. - ContextLoaderUtils ensures uniqueness of @ContextConfiguration elements within a single @ContextHierarchy declaration. - Introduced CacheAwareContextLoaderDelegate that can be used for loading contexts with transparent support for interacting with the context cache -- for example, for retrieving the parent application context in a context hierarchy. - TestContext now delegates to CacheAwareContextLoaderDelegate for loading contexts. - Introduced getParentApplicationContext() in MergedContextConfiguration - The loadContext(MergedContextConfiguration) methods in AbstractGenericContextLoader and AbstractGenericWebContextLoader now set the parent context as appropriate. - Introduced 'hierarchyMode' attribute in @DirtiesContext with a corresponding HierarchyMode enum that defines EXHAUSTIVE and CURRENT_LEVEL cache removal modes. - ContextCache now internally tracks the relationships between contexts that make up a context hierarchy. Furthermore, when a context is removed, if it is part of a context hierarchy all corresponding contexts will be removed from the cache according to the supplied HierarchyMode. - AbstractGenericWebContextLoader will set a loaded context as the ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE in the MockServletContext when context hierarchies are used if the context has no parent or if the context has a parent that is not a WAC. - Where appropriate, updated Javadoc to refer to the ServletTestExecutionListener, which was introduced in 3.2.0. - Updated Javadoc to avoid and/or suppress warnings in spring-test. - Suppressed remaining warnings in code in spring-test. Issue: SPR-5613, SPR-9863
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,21 +16,26 @@
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationUtils}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
@@ -102,23 +107,91 @@ public class AnnotationUtilsTests {
|
||||
assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedClass.class));
|
||||
|
||||
// inherited class-level annotation; note: @Transactional is inherited
|
||||
assertEquals(InheritedAnnotationInterface.class, findAnnotationDeclaringClass(Transactional.class,
|
||||
InheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClass(Transactional.class, InheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClass(Transactional.class, SubInheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationClass.class, findAnnotationDeclaringClass(Transactional.class,
|
||||
InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class, findAnnotationDeclaringClass(Transactional.class,
|
||||
SubInheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Transactional.class, InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Transactional.class, SubInheritedAnnotationClass.class));
|
||||
|
||||
// non-inherited class-level annotation; note: @Order is not inherited,
|
||||
// but findAnnotationDeclaringClass() should still find it.
|
||||
assertEquals(NonInheritedAnnotationInterface.class, findAnnotationDeclaringClass(Order.class,
|
||||
NonInheritedAnnotationInterface.class));
|
||||
// but findAnnotationDeclaringClass() should still find it on classes.
|
||||
assertEquals(NonInheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClass(Order.class, NonInheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClass(Order.class, SubNonInheritedAnnotationInterface.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class, findAnnotationDeclaringClass(Order.class,
|
||||
NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class, findAnnotationDeclaringClass(Order.class,
|
||||
SubNonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Order.class, NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClass(Order.class, SubNonInheritedAnnotationClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationDeclaringClassForTypesWithSingleCandidateType() {
|
||||
|
||||
// no class-level annotation
|
||||
List<Class<? extends Annotation>> transactionalCandidateList = Arrays.<Class<? extends Annotation>> asList(Transactional.class);
|
||||
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList, NonAnnotatedInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList, NonAnnotatedClass.class));
|
||||
|
||||
// inherited class-level annotation; note: @Transactional is inherited
|
||||
assertEquals(InheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList,
|
||||
SubInheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(transactionalCandidateList, SubInheritedAnnotationClass.class));
|
||||
|
||||
// non-inherited class-level annotation; note: @Order is not inherited,
|
||||
// but findAnnotationDeclaringClassForTypes() should still find it on classes.
|
||||
List<Class<? extends Annotation>> orderCandidateList = Arrays.<Class<? extends Annotation>> asList(Order.class);
|
||||
assertEquals(NonInheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(orderCandidateList, NonInheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(orderCandidateList, SubNonInheritedAnnotationInterface.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(orderCandidateList, NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(orderCandidateList, SubNonInheritedAnnotationClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationDeclaringClassForTypesWithMultipleCandidateTypes() {
|
||||
|
||||
List<Class<? extends Annotation>> candidates = Arrays.<Class<? extends Annotation>> asList(Transactional.class,
|
||||
Order.class);
|
||||
|
||||
// no class-level annotation
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, NonAnnotatedInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, NonAnnotatedClass.class));
|
||||
|
||||
// inherited class-level annotation; note: @Transactional is inherited
|
||||
assertEquals(InheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, InheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, SubInheritedAnnotationInterface.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, InheritedAnnotationClass.class));
|
||||
assertEquals(InheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, SubInheritedAnnotationClass.class));
|
||||
|
||||
// non-inherited class-level annotation; note: @Order is not inherited,
|
||||
// but findAnnotationDeclaringClassForTypes() should still find it on classes.
|
||||
assertEquals(NonInheritedAnnotationInterface.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, NonInheritedAnnotationInterface.class));
|
||||
assertNull(findAnnotationDeclaringClassForTypes(candidates, SubNonInheritedAnnotationInterface.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, NonInheritedAnnotationClass.class));
|
||||
assertEquals(NonInheritedAnnotationClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, SubNonInheritedAnnotationClass.class));
|
||||
|
||||
// class hierarchy mixed with @Transactional and @Order declarations
|
||||
assertEquals(TransactionalClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, TransactionalClass.class));
|
||||
assertEquals(TransactionalAndOrderedClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, TransactionalAndOrderedClass.class));
|
||||
assertEquals(TransactionalAndOrderedClass.class,
|
||||
findAnnotationDeclaringClassForTypes(candidates, SubTransactionalAndOrderedClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -216,18 +289,18 @@ public class AnnotationUtilsTests {
|
||||
}
|
||||
|
||||
|
||||
@Component(value="meta1")
|
||||
@Component(value = "meta1")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta1 {
|
||||
}
|
||||
|
||||
@Component(value="meta2")
|
||||
@Component(value = "meta2")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Meta2 {
|
||||
}
|
||||
|
||||
@Meta1
|
||||
@Component(value="local")
|
||||
@Component(value = "local")
|
||||
@Meta2
|
||||
static class HasLocalAndMetaComponentAnnotation {
|
||||
}
|
||||
@@ -332,6 +405,16 @@ public class AnnotationUtilsTests {
|
||||
public static class SubNonInheritedAnnotationClass extends NonInheritedAnnotationClass {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public static class TransactionalClass {
|
||||
}
|
||||
|
||||
@Order
|
||||
public static class TransactionalAndOrderedClass {
|
||||
}
|
||||
|
||||
public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass {
|
||||
}
|
||||
|
||||
public static interface InterfaceWithAnnotatedMethod {
|
||||
|
||||
@@ -353,10 +436,12 @@ public class AnnotationUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class AbstractDoesNotImplementInterfaceWithAnnotatedMethod implements InterfaceWithAnnotatedMethod {
|
||||
public abstract static class AbstractDoesNotImplementInterfaceWithAnnotatedMethod implements
|
||||
InterfaceWithAnnotatedMethod {
|
||||
}
|
||||
|
||||
public static class SubOfAbstractImplementsInterfaceWithAnnotatedMethod extends AbstractDoesNotImplementInterfaceWithAnnotatedMethod {
|
||||
public static class SubOfAbstractImplementsInterfaceWithAnnotatedMethod extends
|
||||
AbstractDoesNotImplementInterfaceWithAnnotatedMethod {
|
||||
|
||||
@Override
|
||||
public void foo() {
|
||||
|
||||
Reference in New Issue
Block a user