Skip to content

Commit a15dc08

Browse files
committed
@import allows for importing regular component classes as well
Issue: SPR-11740
1 parent 1d33fd0 commit a15dc08

File tree

4 files changed

+90
-82
lines changed

4 files changed

+90
-82
lines changed

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

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
import org.springframework.beans.factory.config.BeanDefinition;
3636
import org.springframework.beans.factory.config.BeanDefinitionHolder;
3737
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
38-
import org.springframework.beans.factory.parsing.Location;
39-
import org.springframework.beans.factory.parsing.Problem;
4038
import org.springframework.beans.factory.parsing.ProblemReporter;
4139
import org.springframework.beans.factory.parsing.SourceExtractor;
4240
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
@@ -160,22 +158,18 @@ private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationCl
160158
AnnotationMetadata metadata = configClass.getMetadata();
161159
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
162160

163-
if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
164-
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
165-
configBeanDef.setScope(scopeMetadata.getScopeName());
166-
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
167-
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
168-
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
169-
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
170-
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
171-
configClass.setBeanName(configBeanName);
172-
if (logger.isDebugEnabled()) {
173-
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));
174-
}
175-
}
176-
else {
177-
this.problemReporter.error(
178-
new InvalidConfigurationImportProblem(metadata.getClassName(), configClass.getResource(), metadata));
161+
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
162+
configBeanDef.setScope(scopeMetadata.getScopeName());
163+
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
164+
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
165+
166+
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
167+
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
168+
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
169+
configClass.setBeanName(configBeanName);
170+
171+
if (logger.isDebugEnabled()) {
172+
logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
179173
}
180174
}
181175

@@ -422,21 +416,6 @@ public ConfigurationClassBeanDefinition cloneBeanDefinition() {
422416
}
423417

424418

425-
/**
426-
* Configuration classes must be annotated with {@link Configuration @Configuration} or
427-
* declare at least one {@link Bean @Bean} method.
428-
*/
429-
private static class InvalidConfigurationImportProblem extends Problem {
430-
431-
public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) {
432-
super(String.format("%s was @Import'ed but is not annotated with @Configuration " +
433-
"nor does it declare any @Bean methods; it does not implement ImportSelector " +
434-
"or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements " +
435-
"or do not attempt to @Import it.", className), new Location(resource, metadata));
436-
}
437-
}
438-
439-
440419
/**
441420
* Evaluate {@code @Conditional} annotations, tracking results and taking into
442421
* account 'imported by'.

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -26,22 +26,23 @@
2626
* Indicates one or more {@link Configuration @Configuration} classes to import.
2727
*
2828
* <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
29-
* Only supported for classes annotated with {@code @Configuration} or declaring at least
30-
* one {@link Bean @Bean} method, as well as {@link ImportSelector} and
31-
* {@link ImportBeanDefinitionRegistrar} implementations.
29+
* Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
30+
* {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
31+
* classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
3232
*
33-
* <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes
34-
* should be accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
33+
* <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
34+
* accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
3535
* injection. Either the bean itself can be autowired, or the configuration class instance
36-
* declaring the bean can be autowired. The latter approach allows for explicit,
37-
* IDE-friendly navigation between {@code @Configuration} class methods.
36+
* declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
37+
* navigation between {@code @Configuration} class methods.
3838
*
3939
* <p>May be declared at the class level or as a meta-annotation.
4040
*
4141
* <p>If XML or other non-{@code @Configuration} bean definition resources need to be
42-
* imported, use {@link ImportResource @ImportResource}
42+
* imported, use the {@link ImportResource @ImportResource} annotation instead.
4343
*
4444
* @author Chris Beams
45+
* @author Juergen Hoeller
4546
* @since 3.0
4647
* @see Configuration
4748
* @see ImportSelector
@@ -53,8 +54,9 @@
5354
public @interface Import {
5455

5556
/**
56-
* The @{@link Configuration}, {@link ImportSelector} and/or
57-
* {@link ImportBeanDefinitionRegistrar} classes to import.
57+
* @{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
58+
* or regular component classes to import.
5859
*/
5960
Class<?>[] value();
61+
6062
}

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

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -18,13 +18,13 @@
1818

1919
import org.junit.Test;
2020

21-
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
2221
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
2322
import org.springframework.beans.factory.support.RootBeanDefinition;
2423
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2524
import org.springframework.context.annotation.Bean;
2625
import org.springframework.context.annotation.Configuration;
2726
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
27+
import org.springframework.context.annotation.DependsOn;
2828
import org.springframework.context.annotation.Import;
2929
import org.springframework.tests.sample.beans.ITestBean;
3030
import org.springframework.tests.sample.beans.TestBean;
@@ -36,6 +36,7 @@
3636
* System tests for {@link Import} annotation support.
3737
*
3838
* @author Chris Beams
39+
* @author Juergen Hoeller
3940
*/
4041
public class ImportTests {
4142

@@ -52,6 +53,11 @@ private DefaultListableBeanFactory processConfigurationClasses(Class<?>... class
5253
private void assertBeanDefinitionCount(int expectedCount, Class<?>... classes) {
5354
DefaultListableBeanFactory beanFactory = processConfigurationClasses(classes);
5455
assertThat(beanFactory.getBeanDefinitionCount(), equalTo(expectedCount));
56+
beanFactory.preInstantiateSingletons();
57+
for (Class<?> clazz : classes) {
58+
beanFactory.getBean(clazz);
59+
}
60+
5561
}
5662

5763
@Test
@@ -118,7 +124,6 @@ public ITestBean three() {
118124
public void testImportAnnotationWithTwoLevelRecursion() {
119125
int configClasses = 2;
120126
int beansInClasses = 3;
121-
122127
assertBeanDefinitionCount((configClasses + beansInClasses), AppConfig.class);
123128
}
124129

@@ -149,10 +154,9 @@ public ITestBean dataSourceA() {
149154

150155
@Test
151156
public void testImportAnnotationWithThreeLevelRecursion() {
152-
int configClasses = 3;
157+
int configClasses = 4;
153158
int beansInClasses = 5;
154-
155-
assertBeanDefinitionCount((configClasses + beansInClasses), FirstLevel.class);
159+
assertBeanDefinitionCount(configClasses + beansInClasses, FirstLevel.class);
156160
}
157161

158162
// ------------------------------------------------------------------------
@@ -161,9 +165,7 @@ public void testImportAnnotationWithThreeLevelRecursion() {
161165
public void testImportAnnotationWithMultipleArguments() {
162166
int configClasses = 3;
163167
int beansInClasses = 3;
164-
165-
assertBeanDefinitionCount((configClasses + beansInClasses),
166-
WithMultipleArgumentsToImportAnnotation.class);
168+
assertBeanDefinitionCount((configClasses + beansInClasses), WithMultipleArgumentsToImportAnnotation.class);
167169
}
168170

169171

@@ -179,7 +181,7 @@ public void testImportAnnotationWithMultipleArgumentsResultingInOverriddenBeanDe
179181
}
180182

181183
@Configuration
182-
@Import( { Foo1.class, Foo2.class })
184+
@Import({Foo1.class, Foo2.class})
183185
static class WithMultipleArgumentsThatWillCauseDuplication {
184186
}
185187

@@ -205,7 +207,6 @@ public ITestBean foo() {
205207
public void testImportAnnotationOnInnerClasses() {
206208
int configClasses = 2;
207209
int beansInClasses = 2;
208-
209210
assertBeanDefinitionCount((configClasses + beansInClasses), OuterConfig.InnerConfig.class);
210211
}
211212

@@ -246,7 +247,7 @@ public TestBean m() {
246247
}
247248

248249
@Configuration
249-
@Import(ThirdLevel.class)
250+
@Import({ThirdLevel.class, InitBean.class})
250251
static class SecondLevel {
251252
@Bean
252253
public TestBean n() {
@@ -255,7 +256,12 @@ public TestBean n() {
255256
}
256257

257258
@Configuration
259+
@DependsOn("org.springframework.context.annotation.configuration.ImportTests$InitBean")
258260
static class ThirdLevel {
261+
public ThirdLevel() {
262+
assertTrue(InitBean.initialized);
263+
}
264+
259265
@Bean
260266
public ITestBean thirdLevelA() {
261267
return new TestBean();
@@ -272,8 +278,16 @@ public ITestBean thirdLevelC() {
272278
}
273279
}
274280

281+
static class InitBean {
282+
public static boolean initialized = false;
283+
284+
public InitBean() {
285+
initialized = true;
286+
}
287+
}
288+
275289
@Configuration
276-
@Import( { LeftConfig.class, RightConfig.class })
290+
@Import({LeftConfig.class, RightConfig.class})
277291
static class WithMultipleArgumentsToImportAnnotation {
278292
@Bean
279293
public TestBean m() {
@@ -299,9 +313,11 @@ public ITestBean right() {
299313

300314
// ------------------------------------------------------------------------
301315

302-
@Test(expected=BeanDefinitionParsingException.class)
303-
public void testImportNonConfigurationAnnotationClassCausesError() {
304-
processConfigurationClasses(ConfigAnnotated.class);
316+
@Test
317+
public void testImportNonConfigurationAnnotationClass() {
318+
int configClasses = 2;
319+
int beansInClasses = 0;
320+
assertBeanDefinitionCount((configClasses + beansInClasses), ConfigAnnotated.class);
305321
}
306322

307323
@Configuration

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

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 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.
@@ -19,7 +19,6 @@
1919
import org.junit.Test;
2020

2121
import org.springframework.beans.factory.annotation.Autowired;
22-
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
2322
import org.springframework.context.ApplicationContext;
2423
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2524
import org.springframework.context.annotation.Bean;
@@ -35,10 +34,10 @@
3534
* Unit tests cornering the bug exposed in SPR-6779.
3635
*
3736
* @author Chris Beams
37+
* @author Juergen Hoeller
3838
*/
3939
public class ImportedConfigurationClassEnhancementTests {
4040

41-
4241
@Test
4342
public void autowiredConfigClassIsEnhancedWhenImported() {
4443
autowiredConfigClassIsEnhanced(ConfigThatDoesImport.class);
@@ -77,30 +76,42 @@ private void autowiredConfigClassBeanMethodsRespectScoping(Class<?>... configCla
7776
}
7877

7978

80-
@Test(expected=BeanDefinitionParsingException.class)
79+
@Test
8180
public void importingNonConfigurationClassCausesBeanDefinitionParsingException() {
82-
new AnnotationConfigApplicationContext(ConfigThatImportsNonConfigClass.class);
81+
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigThatImportsNonConfigClass.class);
82+
ConfigThatImportsNonConfigClass config = ctx.getBean(ConfigThatImportsNonConfigClass.class);
83+
assertSame(ctx.getBean(TestBean.class), config.testBean);
8384
}
84-
}
8585

86-
@Configuration
87-
class ConfigToBeAutowired {
88-
public @Bean TestBean testBean() {
89-
return new TestBean();
86+
87+
88+
@Configuration
89+
static class ConfigToBeAutowired {
90+
91+
public @Bean TestBean testBean() {
92+
return new TestBean();
93+
}
9094
}
91-
}
9295

93-
class Config {
94-
@Autowired ConfigToBeAutowired autowiredConfig;
95-
}
96+
static class Config {
9697

97-
@Import(ConfigToBeAutowired.class)
98-
@Configuration
99-
class ConfigThatDoesImport extends Config { }
98+
@Autowired ConfigToBeAutowired autowiredConfig;
99+
}
100+
101+
@Import(ConfigToBeAutowired.class)
102+
@Configuration
103+
static class ConfigThatDoesImport extends Config {
104+
}
105+
106+
@Configuration
107+
static class ConfigThatDoesNotImport extends Config {
108+
}
109+
110+
@Configuration
111+
@Import(TestBean.class)
112+
static class ConfigThatImportsNonConfigClass {
100113

101-
@Configuration
102-
class ConfigThatDoesNotImport extends Config { }
114+
@Autowired TestBean testBean;
115+
}
103116

104-
@Configuration
105-
@Import(TestBean.class)
106-
class ConfigThatImportsNonConfigClass { }
117+
}

0 commit comments

Comments
 (0)