From 4cc3aec5879b0dc2d52612323cddd760634c39c9 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 18 Oct 2023 10:40:23 +0100 Subject: [PATCH] Consider @ComponentScan in imports context cache key Fixes gh-31577 --- .../context/ImportsContextCustomizer.java | 15 ++++++++++- .../ImportsContextCustomizerFactoryTests.java | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizer.java index 447d7849aac..91d042c3cfc 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ImportsContextCustomizer.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; +import java.util.stream.Collectors; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -36,6 +37,7 @@ import org.springframework.boot.context.annotation.DeterminableImports; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -237,7 +239,18 @@ class ImportsContextCustomizer implements ContextCustomizer { Set> seen = new HashSet<>(); collectClassAnnotations(testClass, annotations, seen); Set determinedImports = determineImports(annotations, testClass); - this.key = Collections.unmodifiableSet((determinedImports != null) ? determinedImports : annotations); + if (determinedImports == null) { + this.key = Collections.unmodifiableSet(annotations); + } + else { + Set key = new HashSet<>(); + key.addAll(determinedImports); + Set componentScanning = annotations.stream() + .filter(ComponentScan.class::isInstance) + .collect(Collectors.toSet()); + key.addAll(componentScanning); + this.key = Collections.unmodifiableSet(key); + } } private void collectClassAnnotations(Class classType, Set annotations, Set> seen) { diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ImportsContextCustomizerFactoryTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ImportsContextCustomizerFactoryTests.java index c6d2e53a83a..1138c36b176 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ImportsContextCustomizerFactoryTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/ImportsContextCustomizerFactoryTests.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.stereotype.Component; @@ -74,6 +75,20 @@ class ImportsContextCustomizerFactoryTests { assertThat(customizer3).isEqualTo(customizer4); } + @Test + void contextCustomizerEqualsAndHashCodeConsidersComponentScan() { + ContextCustomizer customizer1 = this.factory + .createContextCustomizer(TestWithImportAndComponentScanOfSomePackage.class, null); + ContextCustomizer customizer2 = this.factory + .createContextCustomizer(TestWithImportAndComponentScanOfSomePackage.class, null); + ContextCustomizer customizer3 = this.factory + .createContextCustomizer(TestWithImportAndComponentScanOfAnotherPackage.class, null); + assertThat(customizer1.hashCode()).isEqualTo(customizer2.hashCode()); + assertThat(customizer1).isEqualTo(customizer2); + assertThat(customizer3.hashCode()).isNotEqualTo(customizer2.hashCode()).isNotEqualTo(customizer1.hashCode()); + assertThat(customizer3).isNotEqualTo(customizer2).isNotEqualTo(customizer1); + } + @Test void getContextCustomizerWhenClassHasBeanMethodsShouldThrowException() { assertThatIllegalStateException() @@ -105,6 +120,18 @@ class ImportsContextCustomizerFactoryTests { } + @Import(ImportedBean.class) + @ComponentScan("some.package") + static class TestWithImportAndComponentScanOfSomePackage { + + } + + @Import(ImportedBean.class) + @ComponentScan("another.package") + static class TestWithImportAndComponentScanOfAnotherPackage { + + } + @MetaImport static class TestWithMetaImport {