From 4581324038094982b266255d4c994f0d7a04d8cb Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:52:49 +0200 Subject: [PATCH] =?UTF-8?q?Polish=20support=20for=20@=E2=81=A0Import=20on?= =?UTF-8?q?=20interfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update @Import Javadoc - Move tests from ImportSelectorTests to ImportTests See gh-34820 --- .../annotation/ConfigurationClassParser.java | 9 +-- .../context/annotation/Import.java | 6 ++ .../annotation/ImportSelectorTests.java | 68 +------------------ .../annotation/configuration/ImportTests.java | 59 +++++++++++++++- 4 files changed, 70 insertions(+), 72 deletions(-) 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 b8086aca92..1f88fbea9f 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 @@ -550,12 +550,13 @@ class ConfigurationClassParser { *
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. - *
As of Spring Framework 7.0, {@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. + *
As of Spring Framework 7.0, {@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
+ * @param visited used to track visited classes and interfaces to prevent infinite
+ * recursion
* @throws IOException if there is any problem reading metadata from the named class
*/
private void collectImports(SourceClass sourceClass, Set As of Spring Framework 7.0, {@code @Import} annotations declared on interfaces
+ * implemented by {@code @Configuration} classes are also supported. Locally declared
+ * {@code @Import} annotations are processed after {@code @Import} annotations on
+ * interfaces, which allows local imports to override beans registered via
+ * {@code @Import} annotations inherited from interfaces.
+ *
* If XML or other non-{@code @Configuration} bean definition resources need to be
* imported, use the {@link ImportResource @ImportResource} annotation instead.
*
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 9741007911..2014c12000 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2025 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -62,7 +62,6 @@ import static org.mockito.Mockito.spy;
*
* @author Phillip Webb
* @author Stephane Nicoll
- * @author Daeho Kwon
*/
@SuppressWarnings("resource")
public class ImportSelectorTests {
@@ -204,71 +203,6 @@ public class ImportSelectorTests {
assertThat(TestImportGroup.environment).isEqualTo(context.getEnvironment());
}
- @Test
- void importAnnotationOnImplementedInterfaceIsRespected() {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
- InterfaceBasedConfig.class);
-
- assertThat(context.getBean(ImportedConfig.class)).isNotNull();
- assertThat(context.getBean(ImportedBean.class)).isNotNull();
- assertThat(context.getBean(ImportedBean.class).name()).isEqualTo("imported");
- }
-
- @Test
- void localImportShouldOverrideInterfaceImport() {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
- OverridingConfig.class);
-
- assertThat(context.getBean(ImportedConfig.class)).isNotNull();
- assertThat(context.getBean(ImportedBean.class)).isNotNull();
- assertThat(context.getBean(ImportedBean.class).name()).isEqualTo("from class");
- }
-
- @Import(ImportedConfig.class)
- interface ConfigImportMarker {
- }
-
- @Configuration
- static class InterfaceBasedConfig implements ConfigImportMarker {
- }
-
- @Configuration
- @Import(OverridingImportedConfig.class)
- static class OverridingConfig implements ConfigImportMarker {
- }
-
- @Configuration
- static class OverridingImportedConfig {
- @Bean
- ImportedBean importedBean() {
- return new ImportedBean("from class");
- }
- }
-
- static class ImportedBean {
-
- private final String name;
-
- ImportedBean() {
- this.name = "imported";
- }
-
- ImportedBean(String name) {
- this.name = name;
- }
-
- String name() {
- return name;
- }
- }
-
- @Configuration
- static class ImportedConfig {
- @Bean
- ImportedBean importedBean() {
- return new ImportedBean();
- }
- }
@Configuration
@Import(SampleImportSelector.class)
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java
index 80172e5380..f46a35af02 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2024 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -38,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Chris Beams
* @author Juergen Hoeller
+ * @author Daeho Kwon
*/
class ImportTests {
@@ -391,4 +392,60 @@ class ImportTests {
assertThat(ctx.getBeansOfType(SiblingImportingConfigB.class)).hasSize(1);
}
+ @Test // gh-34820
+ void importAnnotationOnImplementedInterfaceIsRespected() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(InterfaceBasedConfig.class);
+
+ assertThat(context.getBean(ImportedConfig.class)).isNotNull();
+ assertThat(context.getBean(ImportedBean.class)).hasFieldOrPropertyWithValue("name", "imported");
+
+ context.close();
+ }
+
+ @Test // gh-34820
+ void localImportShouldOverrideInterfaceImport() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(OverridingConfig.class);
+
+ assertThat(context.getBean(ImportedConfig.class)).isNotNull();
+ assertThat(context.getBean(OverridingImportedConfig.class)).isNotNull();
+ assertThat(context.getBean(ImportedBean.class)).hasFieldOrPropertyWithValue("name", "from class");
+
+ context.close();
+ }
+
+
+ record ImportedBean(String name) {
+ }
+
+ @Configuration
+ static class ImportedConfig {
+
+ @Bean
+ ImportedBean importedBean() {
+ return new ImportedBean("imported");
+ }
+ }
+
+ @Configuration
+ static class OverridingImportedConfig {
+
+ @Bean
+ ImportedBean importedBean() {
+ return new ImportedBean("from class");
+ }
+ }
+
+ @Import(ImportedConfig.class)
+ interface ConfigImportMarker {
+ }
+
+ @Configuration
+ static class InterfaceBasedConfig implements ConfigImportMarker {
+ }
+
+ @Configuration
+ @Import(OverridingImportedConfig.class)
+ static class OverridingConfig implements ConfigImportMarker {
+ }
+
}