diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 9543ecf46cc..1231c046b98 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -35,8 +35,6 @@ import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostPr import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; -import org.springframework.beans.factory.parsing.Location; -import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.SourceExtractor; import org.springframework.beans.factory.support.AbstractBeanDefinitionReader; @@ -160,22 +158,18 @@ class ConfigurationClassBeanDefinitionReader { AnnotationMetadata metadata = configClass.getMetadata(); AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); - if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) { - ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); - configBeanDef.setScope(scopeMetadata.getScopeName()); - String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); - AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata); - BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); - definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); - this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition()); - configClass.setBeanName(configBeanName); - if (logger.isDebugEnabled()) { - logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName)); - } - } - else { - this.problemReporter.error( - new InvalidConfigurationImportProblem(metadata.getClassName(), configClass.getResource(), metadata)); + ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); + configBeanDef.setScope(scopeMetadata.getScopeName()); + String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); + AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata); + + BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); + definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); + this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition()); + configClass.setBeanName(configBeanName); + + if (logger.isDebugEnabled()) { + logger.debug("Registered bean definition for imported class '" + configBeanName + "'"); } } @@ -422,21 +416,6 @@ class ConfigurationClassBeanDefinitionReader { } - /** - * Configuration classes must be annotated with {@link Configuration @Configuration} or - * declare at least one {@link Bean @Bean} method. - */ - private static class InvalidConfigurationImportProblem extends Problem { - - public InvalidConfigurationImportProblem(String className, Resource resource, AnnotationMetadata metadata) { - super(String.format("%s was @Import'ed but is not annotated with @Configuration " + - "nor does it declare any @Bean methods; it does not implement ImportSelector " + - "or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements " + - "or do not attempt to @Import it.", className), new Location(resource, metadata)); - } - } - - /** * Evaluate {@code @Conditional} annotations, tracking results and taking into * account 'imported by'. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Import.java b/spring-context/src/main/java/org/springframework/context/annotation/Import.java index 6c03d55f696..e70c68ef42d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Import.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Import.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -26,22 +26,23 @@ import java.lang.annotation.Target; * Indicates one or more {@link Configuration @Configuration} classes to import. * *

Provides functionality equivalent to the {@code } element in Spring XML. - * Only supported for classes annotated with {@code @Configuration} or declaring at least - * one {@link Bean @Bean} method, as well as {@link ImportSelector} and - * {@link ImportBeanDefinitionRegistrar} implementations. + * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and + * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component + * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}). * - *

{@code @Bean} definitions declared in imported {@code @Configuration} classes - * should be accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired} + *

{@code @Bean} definitions declared in imported {@code @Configuration} classes should be + * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired} * injection. Either the bean itself can be autowired, or the configuration class instance - * declaring the bean can be autowired. The latter approach allows for explicit, - * IDE-friendly navigation between {@code @Configuration} class methods. + * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly + * navigation between {@code @Configuration} class methods. * *

May be declared at the class level or as a meta-annotation. * *

If XML or other non-{@code @Configuration} bean definition resources need to be - * imported, use {@link ImportResource @ImportResource} + * imported, use the {@link ImportResource @ImportResource} annotation instead. * * @author Chris Beams + * @author Juergen Hoeller * @since 3.0 * @see Configuration * @see ImportSelector @@ -53,8 +54,9 @@ import java.lang.annotation.Target; public @interface Import { /** - * The @{@link Configuration}, {@link ImportSelector} and/or - * {@link ImportBeanDefinitionRegistrar} classes to import. + * @{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} + * or regular component classes to import. */ Class[] value(); + } 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 534cd886e87..2fb9d4d6a89 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-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -18,13 +18,13 @@ package org.springframework.context.annotation.configuration; import org.junit.Test; -import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ConfigurationClassPostProcessor; +import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.TestBean; @@ -36,6 +36,7 @@ import static org.junit.Assert.*; * System tests for {@link Import} annotation support. * * @author Chris Beams + * @author Juergen Hoeller */ public class ImportTests { @@ -52,6 +53,11 @@ public class ImportTests { private void assertBeanDefinitionCount(int expectedCount, Class... classes) { DefaultListableBeanFactory beanFactory = processConfigurationClasses(classes); assertThat(beanFactory.getBeanDefinitionCount(), equalTo(expectedCount)); + beanFactory.preInstantiateSingletons(); + for (Class clazz : classes) { + beanFactory.getBean(clazz); + } + } @Test @@ -118,7 +124,6 @@ public class ImportTests { public void testImportAnnotationWithTwoLevelRecursion() { int configClasses = 2; int beansInClasses = 3; - assertBeanDefinitionCount((configClasses + beansInClasses), AppConfig.class); } @@ -149,10 +154,9 @@ public class ImportTests { @Test public void testImportAnnotationWithThreeLevelRecursion() { - int configClasses = 3; + int configClasses = 4; int beansInClasses = 5; - - assertBeanDefinitionCount((configClasses + beansInClasses), FirstLevel.class); + assertBeanDefinitionCount(configClasses + beansInClasses, FirstLevel.class); } // ------------------------------------------------------------------------ @@ -161,9 +165,7 @@ public class ImportTests { public void testImportAnnotationWithMultipleArguments() { int configClasses = 3; int beansInClasses = 3; - - assertBeanDefinitionCount((configClasses + beansInClasses), - WithMultipleArgumentsToImportAnnotation.class); + assertBeanDefinitionCount((configClasses + beansInClasses), WithMultipleArgumentsToImportAnnotation.class); } @@ -179,7 +181,7 @@ public class ImportTests { } @Configuration - @Import( { Foo1.class, Foo2.class }) + @Import({Foo1.class, Foo2.class}) static class WithMultipleArgumentsThatWillCauseDuplication { } @@ -205,7 +207,6 @@ public class ImportTests { public void testImportAnnotationOnInnerClasses() { int configClasses = 2; int beansInClasses = 2; - assertBeanDefinitionCount((configClasses + beansInClasses), OuterConfig.InnerConfig.class); } @@ -246,7 +247,7 @@ public class ImportTests { } @Configuration - @Import(ThirdLevel.class) + @Import({ThirdLevel.class, InitBean.class}) static class SecondLevel { @Bean public TestBean n() { @@ -255,7 +256,12 @@ public class ImportTests { } @Configuration + @DependsOn("org.springframework.context.annotation.configuration.ImportTests$InitBean") static class ThirdLevel { + public ThirdLevel() { + assertTrue(InitBean.initialized); + } + @Bean public ITestBean thirdLevelA() { return new TestBean(); @@ -272,8 +278,16 @@ public class ImportTests { } } + static class InitBean { + public static boolean initialized = false; + + public InitBean() { + initialized = true; + } + } + @Configuration - @Import( { LeftConfig.class, RightConfig.class }) + @Import({LeftConfig.class, RightConfig.class}) static class WithMultipleArgumentsToImportAnnotation { @Bean public TestBean m() { @@ -299,9 +313,11 @@ public class ImportTests { // ------------------------------------------------------------------------ - @Test(expected=BeanDefinitionParsingException.class) - public void testImportNonConfigurationAnnotationClassCausesError() { - processConfigurationClasses(ConfigAnnotated.class); + @Test + public void testImportNonConfigurationAnnotationClass() { + int configClasses = 2; + int beansInClasses = 0; + assertBeanDefinitionCount((configClasses + beansInClasses), ConfigAnnotated.class); } @Configuration diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java index 334a7bad0cc..e2d0c781c85 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -19,7 +19,6 @@ package org.springframework.context.annotation.configuration; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -35,10 +34,10 @@ import static org.junit.Assert.*; * Unit tests cornering the bug exposed in SPR-6779. * * @author Chris Beams + * @author Juergen Hoeller */ public class ImportedConfigurationClassEnhancementTests { - @Test public void autowiredConfigClassIsEnhancedWhenImported() { autowiredConfigClassIsEnhanced(ConfigThatDoesImport.class); @@ -77,30 +76,42 @@ public class ImportedConfigurationClassEnhancementTests { } - @Test(expected=BeanDefinitionParsingException.class) + @Test public void importingNonConfigurationClassCausesBeanDefinitionParsingException() { - new AnnotationConfigApplicationContext(ConfigThatImportsNonConfigClass.class); + ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigThatImportsNonConfigClass.class); + ConfigThatImportsNonConfigClass config = ctx.getBean(ConfigThatImportsNonConfigClass.class); + assertSame(ctx.getBean(TestBean.class), config.testBean); } -} -@Configuration -class ConfigToBeAutowired { - public @Bean TestBean testBean() { - return new TestBean(); + + + @Configuration + static class ConfigToBeAutowired { + + public @Bean TestBean testBean() { + return new TestBean(); + } } -} -class Config { - @Autowired ConfigToBeAutowired autowiredConfig; -} + static class Config { -@Import(ConfigToBeAutowired.class) -@Configuration -class ConfigThatDoesImport extends Config { } + @Autowired ConfigToBeAutowired autowiredConfig; + } + + @Import(ConfigToBeAutowired.class) + @Configuration + static class ConfigThatDoesImport extends Config { + } + + @Configuration + static class ConfigThatDoesNotImport extends Config { + } + + @Configuration + @Import(TestBean.class) + static class ConfigThatImportsNonConfigClass { -@Configuration -class ConfigThatDoesNotImport extends Config { } + @Autowired TestBean testBean; + } -@Configuration -@Import(TestBean.class) -class ConfigThatImportsNonConfigClass { } \ No newline at end of file +}