mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Ensure Bean Overrides are discovered once in hierarchies
Prior to this commit, bean overrides (such as @MockitoBean, etc.) were discovered multiple times if they were declared: - at the type-level on an interface that is implemented at more than one level in the type hierarchy, the enclosing class hierarchy, or a combination of the type and enclosing class hierarchies. or - on a field declared in a class which can be reached multiple times while traversing the type and enclosing class hierarchies in scenarios such as the following: the class (X) in which the field is declared is a supertype of an enclosing type of the test class, and X is also an enclosing type of a supertype of the test class. Such scenarios resulted in an IllegalStateException stating that a duplicate BeanOverrideHandler was discovered. To address that, this commit revises the search algorithm in BeanOverrideHandler so that all types (superclasses, enclosing classes, and implemented interfaces) are only visited once while traversing the type and enclosing class hierarchies in search of bean override handlers. See gh-33925 See gh-34324 Closes gh-34844
This commit is contained in:
+10
-8
@@ -184,30 +184,32 @@ public abstract class BeanOverrideHandler {
|
||||
* @param testClass the original test class
|
||||
* @param handlers the list of handlers found
|
||||
* @param localFieldsOnly whether to search only on local fields within the type hierarchy
|
||||
* @param visitedEnclosingClasses the set of enclosing classes already visited
|
||||
* @param visitedTypes the set of types already visited
|
||||
* @since 6.2.2
|
||||
*/
|
||||
private static void findHandlers(Class<?> clazz, Class<?> testClass, List<BeanOverrideHandler> handlers,
|
||||
boolean localFieldsOnly, Set<Class<?>> visitedEnclosingClasses) {
|
||||
boolean localFieldsOnly, Set<Class<?>> visitedTypes) {
|
||||
|
||||
// 0) Ensure that we do not process the same class or interface multiple times.
|
||||
if (!visitedTypes.add(clazz)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) Search enclosing class hierarchy.
|
||||
if (!localFieldsOnly && TestContextAnnotationUtils.searchEnclosingClass(clazz)) {
|
||||
Class<?> enclosingClass = clazz.getEnclosingClass();
|
||||
if (visitedEnclosingClasses.add(enclosingClass)) {
|
||||
findHandlers(enclosingClass, testClass, handlers, localFieldsOnly, visitedEnclosingClasses);
|
||||
}
|
||||
findHandlers(clazz.getEnclosingClass(), testClass, handlers, localFieldsOnly, visitedTypes);
|
||||
}
|
||||
|
||||
// 2) Search class hierarchy.
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass != null && superclass != Object.class) {
|
||||
findHandlers(superclass, testClass, handlers, localFieldsOnly, visitedEnclosingClasses);
|
||||
findHandlers(superclass, testClass, handlers, localFieldsOnly, visitedTypes);
|
||||
}
|
||||
|
||||
if (!localFieldsOnly) {
|
||||
// 3) Search interfaces.
|
||||
for (Class<?> ifc : clazz.getInterfaces()) {
|
||||
findHandlers(ifc, testClass, handlers, localFieldsOnly, visitedEnclosingClasses);
|
||||
findHandlers(ifc, testClass, handlers, localFieldsOnly, visitedTypes);
|
||||
}
|
||||
|
||||
// 4) Process current class.
|
||||
|
||||
Reference in New Issue
Block a user