Skip to content

Commit 4581324

Browse files
committed
Polish support for @⁠Import on interfaces
- Update @⁠Import Javadoc - Move tests from ImportSelectorTests to ImportTests See gh-34820
1 parent a4d5800 commit 4581324

File tree

4 files changed

+70
-72
lines changed

4 files changed

+70
-72
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -550,12 +550,13 @@ private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException
550550
* <p>For example, it is common for a {@code @Configuration} class to declare direct
551551
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
552552
* annotation.
553-
* <p>As of Spring Framework 7.0, {@code @Import} annotations declared on interfaces implemented by
554-
* the configuration class are also considered. This allows imports to be triggered
555-
* indirectly via marker interfaces or shared base interfaces.
553+
* <p>As of Spring Framework 7.0, {@code @Import} annotations declared on interfaces
554+
* implemented by the configuration class are also considered. This allows imports to
555+
* be triggered indirectly via marker interfaces or shared base interfaces.
556556
* @param sourceClass the class to search
557557
* @param imports the imports collected so far
558-
* @param visited used to track visited classes to prevent infinite recursion
558+
* @param visited used to track visited classes and interfaces to prevent infinite
559+
* recursion
559560
* @throws IOException if there is any problem reading metadata from the named class
560561
*/
561562
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)

spring-context/src/main/java/org/springframework/context/annotation/Import.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
* directly declared imports to override beans registered via {@code @Import}
4848
* meta-annotations.
4949
*
50+
* <p>As of Spring Framework 7.0, {@code @Import} annotations declared on interfaces
51+
* implemented by {@code @Configuration} classes are also supported. Locally declared
52+
* {@code @Import} annotations are processed after {@code @Import} annotations on
53+
* interfaces, which allows local imports to override beans registered via
54+
* {@code @Import} annotations inherited from interfaces.
55+
*
5056
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
5157
* imported, use the {@link ImportResource @ImportResource} annotation instead.
5258
*

spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2025 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,7 +62,6 @@
6262
*
6363
* @author Phillip Webb
6464
* @author Stephane Nicoll
65-
* @author Daeho Kwon
6665
*/
6766
@SuppressWarnings("resource")
6867
public class ImportSelectorTests {
@@ -204,71 +203,6 @@ void invokeAwareMethodsInImportGroup() {
204203
assertThat(TestImportGroup.environment).isEqualTo(context.getEnvironment());
205204
}
206205

207-
@Test
208-
void importAnnotationOnImplementedInterfaceIsRespected() {
209-
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
210-
InterfaceBasedConfig.class);
211-
212-
assertThat(context.getBean(ImportedConfig.class)).isNotNull();
213-
assertThat(context.getBean(ImportedBean.class)).isNotNull();
214-
assertThat(context.getBean(ImportedBean.class).name()).isEqualTo("imported");
215-
}
216-
217-
@Test
218-
void localImportShouldOverrideInterfaceImport() {
219-
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
220-
OverridingConfig.class);
221-
222-
assertThat(context.getBean(ImportedConfig.class)).isNotNull();
223-
assertThat(context.getBean(ImportedBean.class)).isNotNull();
224-
assertThat(context.getBean(ImportedBean.class).name()).isEqualTo("from class");
225-
}
226-
227-
@Import(ImportedConfig.class)
228-
interface ConfigImportMarker {
229-
}
230-
231-
@Configuration
232-
static class InterfaceBasedConfig implements ConfigImportMarker {
233-
}
234-
235-
@Configuration
236-
@Import(OverridingImportedConfig.class)
237-
static class OverridingConfig implements ConfigImportMarker {
238-
}
239-
240-
@Configuration
241-
static class OverridingImportedConfig {
242-
@Bean
243-
ImportedBean importedBean() {
244-
return new ImportedBean("from class");
245-
}
246-
}
247-
248-
static class ImportedBean {
249-
250-
private final String name;
251-
252-
ImportedBean() {
253-
this.name = "imported";
254-
}
255-
256-
ImportedBean(String name) {
257-
this.name = name;
258-
}
259-
260-
String name() {
261-
return name;
262-
}
263-
}
264-
265-
@Configuration
266-
static class ImportedConfig {
267-
@Bean
268-
ImportedBean importedBean() {
269-
return new ImportedBean();
270-
}
271-
}
272206

273207
@Configuration
274208
@Import(SampleImportSelector.class)

spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportTests.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
3838
*
3939
* @author Chris Beams
4040
* @author Juergen Hoeller
41+
* @author Daeho Kwon
4142
*/
4243
class ImportTests {
4344

@@ -391,4 +392,60 @@ void importedConfigOverridesScanned() {
391392
assertThat(ctx.getBeansOfType(SiblingImportingConfigB.class)).hasSize(1);
392393
}
393394

395+
@Test // gh-34820
396+
void importAnnotationOnImplementedInterfaceIsRespected() {
397+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(InterfaceBasedConfig.class);
398+
399+
assertThat(context.getBean(ImportedConfig.class)).isNotNull();
400+
assertThat(context.getBean(ImportedBean.class)).hasFieldOrPropertyWithValue("name", "imported");
401+
402+
context.close();
403+
}
404+
405+
@Test // gh-34820
406+
void localImportShouldOverrideInterfaceImport() {
407+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(OverridingConfig.class);
408+
409+
assertThat(context.getBean(ImportedConfig.class)).isNotNull();
410+
assertThat(context.getBean(OverridingImportedConfig.class)).isNotNull();
411+
assertThat(context.getBean(ImportedBean.class)).hasFieldOrPropertyWithValue("name", "from class");
412+
413+
context.close();
414+
}
415+
416+
417+
record ImportedBean(String name) {
418+
}
419+
420+
@Configuration
421+
static class ImportedConfig {
422+
423+
@Bean
424+
ImportedBean importedBean() {
425+
return new ImportedBean("imported");
426+
}
427+
}
428+
429+
@Configuration
430+
static class OverridingImportedConfig {
431+
432+
@Bean
433+
ImportedBean importedBean() {
434+
return new ImportedBean("from class");
435+
}
436+
}
437+
438+
@Import(ImportedConfig.class)
439+
interface ConfigImportMarker {
440+
}
441+
442+
@Configuration
443+
static class InterfaceBasedConfig implements ConfigImportMarker {
444+
}
445+
446+
@Configuration
447+
@Import(OverridingImportedConfig.class)
448+
static class OverridingConfig implements ConfigImportMarker {
449+
}
450+
394451
}

0 commit comments

Comments
 (0)