From 6ff1e2d622049abb4dbeacd878c4a6eaeafd46d9 Mon Sep 17 00:00:00 2001 From: Daeho Kwon Date: Fri, 25 Apr 2025 03:29:00 +0900 Subject: [PATCH] Support `@Import` on interfaces Closes gh-34805 Signed-off-by: Daeho Kwon --- .../annotation/ConfigurationClassParser.java | 8 +++++ .../annotation/ImportSelectorTests.java | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 104e2fdd2102..ca514a593db9 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -98,6 +98,7 @@ * @author Phillip Webb * @author Sam Brannen * @author Stephane Nicoll + * @author Daeho Kwon * @since 3.0 * @see ConfigurationClassBeanDefinitionReader */ @@ -549,6 +550,9 @@ private Set getImports(SourceClass sourceClass) throws IOException *

For example, it is common for a {@code @Configuration} class to declare direct * {@code @Import}s in addition to meta-imports originating from an {@code @Enable} * annotation. + *

In addition, {@code @Import} annotations declared on interfaces implemented by + * the configuration class are also considered. This allows imports to be triggered + * indirectly via marker interfaces or shared base interfaces. * @param sourceClass the class to search * @param imports the imports collected so far * @param visited used to track visited classes to prevent infinite recursion @@ -565,6 +569,10 @@ private void collectImports(SourceClass sourceClass, Set imports, S } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); + + for (SourceClass ifc : sourceClass.getInterfaces()) { + collectImports(ifc, imports, visited); + } } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java index 2014c12000b2..fba0fab9426a 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java @@ -62,6 +62,7 @@ * * @author Phillip Webb * @author Stephane Nicoll + * @author Daeho Kwon */ @SuppressWarnings("resource") public class ImportSelectorTests { @@ -203,6 +204,38 @@ void invokeAwareMethodsInImportGroup() { assertThat(TestImportGroup.environment).isEqualTo(context.getEnvironment()); } + @Test + void importAnnotationOnImplementedInterfaceIsRespected() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(InterfaceBasedConfig.class); + context.refresh(); + + assertThat(context.getBean(ImportedConfig.class)).isNotNull(); + assertThat(context.getBean(ImportedBean.class)).isNotNull(); + assertThat(context.getBean(ImportedBean.class).name()).isEqualTo("imported"); + } + + @Import(ImportedConfig.class) + interface ConfigImportMarker { + } + + @Configuration + static class InterfaceBasedConfig implements ConfigImportMarker { + } + + static class ImportedBean { + String name() { + return "imported"; + } + } + + @Configuration + static class ImportedConfig { + @Bean + ImportedBean importedBean() { + return new ImportedBean(); + } + } @Configuration @Import(SampleImportSelector.class)