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 98333791f26..1e32272ec5a 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 @@ -72,6 +72,8 @@ class ConfigurationClassBeanDefinitionReader { private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class); + private static final ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); + private final BeanDefinitionRegistry registry; private final SourceExtractor sourceExtractor; @@ -154,10 +156,16 @@ class ConfigurationClassBeanDefinitionReader { */ private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { AnnotationMetadata metadata = configClass.getMetadata(); - BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); + 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); - this.registry.registerBeanDefinition(configBeanName, configBeanDef); + 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)); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java b/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java index f56510d2eba..9d1e816857d 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/NestedConfigurationClassTests.java @@ -58,12 +58,15 @@ public class NestedConfigurationClassTests { ctx.register(L0Config.class); ctx.refresh(); + assertFalse(ctx.getBeanFactory().containsSingleton("nestedConfigurationClassTests.L0Config")); ctx.getBean(L0Config.class); ctx.getBean("l0Bean"); + assertTrue(ctx.getBeanFactory().containsSingleton(L0Config.L1Config.class.getName())); ctx.getBean(L0Config.L1Config.class); ctx.getBean("l1Bean"); + assertFalse(ctx.getBeanFactory().containsSingleton(L0Config.L1Config.L2Config.class.getName())); ctx.getBean(L0Config.L1Config.L2Config.class); ctx.getBean("l2Bean"); @@ -71,14 +74,38 @@ public class NestedConfigurationClassTests { assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-l0")); } + @Test + public void twoLevelsInLiteMode() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(L0ConfigLight.class); + ctx.refresh(); + + assertFalse(ctx.getBeanFactory().containsSingleton("nestedConfigurationClassTests.L0ConfigLight")); + ctx.getBean(L0ConfigLight.class); + ctx.getBean("l0Bean"); + + assertTrue(ctx.getBeanFactory().containsSingleton(L0ConfigLight.L1ConfigLight.class.getName())); + ctx.getBean(L0ConfigLight.L1ConfigLight.class); + ctx.getBean("l1Bean"); + + assertFalse(ctx.getBeanFactory().containsSingleton(L0ConfigLight.L1ConfigLight.L2ConfigLight.class.getName())); + ctx.getBean(L0ConfigLight.L1ConfigLight.L2ConfigLight.class); + ctx.getBean("l2Bean"); + + // ensure that override order is correct + assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-l0")); + } + @Test public void twoLevelsDeepWithInheritance() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(S1Config.class); ctx.refresh(); - ctx.getBean(S1Config.class); - ctx.getBean("l0Bean"); + S1Config config = ctx.getBean(S1Config.class); + assertTrue(config != ctx.getBean(S1Config.class)); + TestBean tb = ctx.getBean("l0Bean", TestBean.class); + assertTrue(tb == ctx.getBean("l0Bean", TestBean.class)); ctx.getBean(L0Config.L1Config.class); ctx.getBean("l1Bean"); @@ -86,27 +113,71 @@ public class NestedConfigurationClassTests { ctx.getBean(L0Config.L1Config.L2Config.class); ctx.getBean("l2Bean"); - // ensure that override order is correct - assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-s1")); + // ensure that override order is correct and that it is a singleton + TestBean ob = ctx.getBean("overrideBean", TestBean.class); + assertThat(ob.getName(), is("override-s1")); + assertTrue(ob == ctx.getBean("overrideBean", TestBean.class)); + + TestBean pb1 = ctx.getBean("prototypeBean", TestBean.class); + TestBean pb2 = ctx.getBean("prototypeBean", TestBean.class); + assertTrue(pb1 != pb2); + assertTrue(pb1.getFriends().iterator().next() != pb2.getFriends().iterator().next()); } @Test - public void twoLevelsInLiteMode() { + public void twoLevelsDeepWithInheritanceThroughImport() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(L0ConfigLight.class); + ctx.register(S1Importer.class); ctx.refresh(); - ctx.getBean(L0ConfigLight.class); - ctx.getBean("l0Bean"); + S1Config config = ctx.getBean(S1Config.class); + assertTrue(config != ctx.getBean(S1Config.class)); + TestBean tb = ctx.getBean("l0Bean", TestBean.class); + assertTrue(tb == ctx.getBean("l0Bean", TestBean.class)); - ctx.getBean(L0ConfigLight.L1ConfigLight.class); + ctx.getBean(L0Config.L1Config.class); ctx.getBean("l1Bean"); - ctx.getBean(L0ConfigLight.L1ConfigLight.L2ConfigLight.class); + ctx.getBean(L0Config.L1Config.L2Config.class); ctx.getBean("l2Bean"); - // ensure that override order is correct - assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-l0")); + // ensure that override order is correct and that it is a singleton + TestBean ob = ctx.getBean("overrideBean", TestBean.class); + assertThat(ob.getName(), is("override-s1")); + assertTrue(ob == ctx.getBean("overrideBean", TestBean.class)); + + TestBean pb1 = ctx.getBean("prototypeBean", TestBean.class); + TestBean pb2 = ctx.getBean("prototypeBean", TestBean.class); + assertTrue(pb1 != pb2); + assertTrue(pb1.getFriends().iterator().next() != pb2.getFriends().iterator().next()); + } + + @Test + public void twoLevelsDeepWithInheritanceAndScopedProxy() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(S1ImporterWithProxy.class); + ctx.refresh(); + + S1ConfigWithProxy config = ctx.getBean(S1ConfigWithProxy.class); + assertTrue(config == ctx.getBean(S1ConfigWithProxy.class)); + TestBean tb = ctx.getBean("l0Bean", TestBean.class); + assertTrue(tb == ctx.getBean("l0Bean", TestBean.class)); + + ctx.getBean(L0Config.L1Config.class); + ctx.getBean("l1Bean"); + + ctx.getBean(L0Config.L1Config.L2Config.class); + ctx.getBean("l2Bean"); + + // ensure that override order is correct and that it is a singleton + TestBean ob = ctx.getBean("overrideBean", TestBean.class); + assertThat(ob.getName(), is("override-s1")); + assertTrue(ob == ctx.getBean("overrideBean", TestBean.class)); + + TestBean pb1 = ctx.getBean("prototypeBean", TestBean.class); + TestBean pb2 = ctx.getBean("prototypeBean", TestBean.class); + assertTrue(pb1 != pb2); + assertTrue(pb1.getFriends().iterator().next() != pb2.getFriends().iterator().next()); } @Test @@ -115,21 +186,34 @@ public class NestedConfigurationClassTests { ctx.register(L0ConfigEmpty.class); ctx.refresh(); - ctx.getBean(L0ConfigEmpty.class); - ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.class); - ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.L2ConfigEmpty.class); + assertFalse(ctx.getBeanFactory().containsSingleton("l0ConfigEmpty")); + Object l0i1 = ctx.getBean(L0ConfigEmpty.class); + Object l0i2 = ctx.getBean(L0ConfigEmpty.class); + assertTrue(l0i1 == l0i2); + + Object l1i1 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.class); + Object l1i2 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.class); + assertTrue(l1i1 != l1i2); + + Object l2i1 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.L2ConfigEmpty.class); + Object l2i2 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.L2ConfigEmpty.class); + assertTrue(l2i1 == l2i2); + assertNotEquals(l2i1.toString(), l2i2.toString()); } @Configuration + @Lazy static class L0Config { @Bean + @Lazy public TestBean l0Bean() { return new TestBean("l0"); } @Bean + @Lazy public TestBean overrideBean() { return new TestBean("override-l0"); } @@ -148,14 +232,17 @@ public class NestedConfigurationClassTests { } @Configuration + @Lazy protected static class L2Config { @Bean + @Lazy public TestBean l2Bean() { return new TestBean("l2"); } @Bean + @Lazy public TestBean overrideBean() { return new TestBean("override-l2"); } @@ -165,14 +252,17 @@ public class NestedConfigurationClassTests { @Component + @Lazy static class L0ConfigLight { @Bean + @Lazy public TestBean l0Bean() { return new TestBean("l0"); } @Bean + @Lazy public TestBean overrideBean() { return new TestBean("override-l0"); } @@ -191,14 +281,17 @@ public class NestedConfigurationClassTests { } @Component + @Lazy protected static class L2ConfigLight { @Bean + @Lazy public TestBean l2Bean() { return new TestBean("l2"); } @Bean + @Lazy public TestBean overrideBean() { return new TestBean("override-l2"); } @@ -207,20 +300,8 @@ public class NestedConfigurationClassTests { } - @Component - static class L0ConfigEmpty { - - @Component - static class L1ConfigEmpty { - - @Component - protected static class L2ConfigEmpty { - } - } - } - - @Configuration + @Scope("prototype") static class S1Config extends L0Config { @Override @@ -228,6 +309,60 @@ public class NestedConfigurationClassTests { public TestBean overrideBean() { return new TestBean("override-s1"); } + + @Bean + @Scope("prototype") + public TestBean prototypeBean() { + TestBean tb = new TestBean("override-s1"); + tb.getFriends().add(this); + return tb; + } + } + + + @Configuration + @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) + static class S1ConfigWithProxy extends L0Config { + + @Override + @Bean + public TestBean overrideBean() { + return new TestBean("override-s1"); + } + + @Bean + @Scope("prototype") + public TestBean prototypeBean() { + TestBean tb = new TestBean("override-s1"); + tb.getFriends().add(this); + return tb; + } + } + + + @Import(S1Config.class) + static class S1Importer { + } + + + @Import(S1ConfigWithProxy.class) + static class S1ImporterWithProxy { + } + + + @Component + @Lazy + static class L0ConfigEmpty { + + @Component + @Scope("prototype") + static class L1ConfigEmpty { + + @Component + @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) + protected static class L2ConfigEmpty { + } + } } }