Skip to content

Commit c5c5473

Browse files
committed
Common annotation processing for imported and nested classes within configuration classes
Issue: SPR-12486
1 parent c94d584 commit c5c5473

File tree

2 files changed

+168
-25
lines changed

2 files changed

+168
-25
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class ConfigurationClassBeanDefinitionReader {
7272

7373
private static final Log logger = LogFactory.getLog(ConfigurationClassBeanDefinitionReader.class);
7474

75+
private static final ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
76+
7577
private final BeanDefinitionRegistry registry;
7678

7779
private final SourceExtractor sourceExtractor;
@@ -154,10 +156,16 @@ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configC
154156
*/
155157
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
156158
AnnotationMetadata metadata = configClass.getMetadata();
157-
BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
159+
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
160+
158161
if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
162+
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
163+
configBeanDef.setScope(scopeMetadata.getScopeName());
159164
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
160-
this.registry.registerBeanDefinition(configBeanName, configBeanDef);
165+
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
166+
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
167+
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
168+
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
161169
configClass.setBeanName(configBeanName);
162170
if (logger.isDebugEnabled()) {
163171
logger.debug(String.format("Registered bean definition for imported @Configuration class %s", configBeanName));

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

Lines changed: 158 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,55 +58,126 @@ public void twoLevelsDeep() {
5858
ctx.register(L0Config.class);
5959
ctx.refresh();
6060

61+
assertFalse(ctx.getBeanFactory().containsSingleton("nestedConfigurationClassTests.L0Config"));
6162
ctx.getBean(L0Config.class);
6263
ctx.getBean("l0Bean");
6364

65+
assertTrue(ctx.getBeanFactory().containsSingleton(L0Config.L1Config.class.getName()));
6466
ctx.getBean(L0Config.L1Config.class);
6567
ctx.getBean("l1Bean");
6668

69+
assertFalse(ctx.getBeanFactory().containsSingleton(L0Config.L1Config.L2Config.class.getName()));
6770
ctx.getBean(L0Config.L1Config.L2Config.class);
6871
ctx.getBean("l2Bean");
6972

7073
// ensure that override order is correct
7174
assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-l0"));
7275
}
7376

77+
@Test
78+
public void twoLevelsInLiteMode() {
79+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
80+
ctx.register(L0ConfigLight.class);
81+
ctx.refresh();
82+
83+
assertFalse(ctx.getBeanFactory().containsSingleton("nestedConfigurationClassTests.L0ConfigLight"));
84+
ctx.getBean(L0ConfigLight.class);
85+
ctx.getBean("l0Bean");
86+
87+
assertTrue(ctx.getBeanFactory().containsSingleton(L0ConfigLight.L1ConfigLight.class.getName()));
88+
ctx.getBean(L0ConfigLight.L1ConfigLight.class);
89+
ctx.getBean("l1Bean");
90+
91+
assertFalse(ctx.getBeanFactory().containsSingleton(L0ConfigLight.L1ConfigLight.L2ConfigLight.class.getName()));
92+
ctx.getBean(L0ConfigLight.L1ConfigLight.L2ConfigLight.class);
93+
ctx.getBean("l2Bean");
94+
95+
// ensure that override order is correct
96+
assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-l0"));
97+
}
98+
7499
@Test
75100
public void twoLevelsDeepWithInheritance() {
76101
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
77102
ctx.register(S1Config.class);
78103
ctx.refresh();
79104

80-
ctx.getBean(S1Config.class);
81-
ctx.getBean("l0Bean");
105+
S1Config config = ctx.getBean(S1Config.class);
106+
assertTrue(config != ctx.getBean(S1Config.class));
107+
TestBean tb = ctx.getBean("l0Bean", TestBean.class);
108+
assertTrue(tb == ctx.getBean("l0Bean", TestBean.class));
82109

83110
ctx.getBean(L0Config.L1Config.class);
84111
ctx.getBean("l1Bean");
85112

86113
ctx.getBean(L0Config.L1Config.L2Config.class);
87114
ctx.getBean("l2Bean");
88115

89-
// ensure that override order is correct
90-
assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-s1"));
116+
// ensure that override order is correct and that it is a singleton
117+
TestBean ob = ctx.getBean("overrideBean", TestBean.class);
118+
assertThat(ob.getName(), is("override-s1"));
119+
assertTrue(ob == ctx.getBean("overrideBean", TestBean.class));
120+
121+
TestBean pb1 = ctx.getBean("prototypeBean", TestBean.class);
122+
TestBean pb2 = ctx.getBean("prototypeBean", TestBean.class);
123+
assertTrue(pb1 != pb2);
124+
assertTrue(pb1.getFriends().iterator().next() != pb2.getFriends().iterator().next());
91125
}
92126

93127
@Test
94-
public void twoLevelsInLiteMode() {
128+
public void twoLevelsDeepWithInheritanceThroughImport() {
95129
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
96-
ctx.register(L0ConfigLight.class);
130+
ctx.register(S1Importer.class);
97131
ctx.refresh();
98132

99-
ctx.getBean(L0ConfigLight.class);
100-
ctx.getBean("l0Bean");
133+
S1Config config = ctx.getBean(S1Config.class);
134+
assertTrue(config != ctx.getBean(S1Config.class));
135+
TestBean tb = ctx.getBean("l0Bean", TestBean.class);
136+
assertTrue(tb == ctx.getBean("l0Bean", TestBean.class));
101137

102-
ctx.getBean(L0ConfigLight.L1ConfigLight.class);
138+
ctx.getBean(L0Config.L1Config.class);
103139
ctx.getBean("l1Bean");
104140

105-
ctx.getBean(L0ConfigLight.L1ConfigLight.L2ConfigLight.class);
141+
ctx.getBean(L0Config.L1Config.L2Config.class);
106142
ctx.getBean("l2Bean");
107143

108-
// ensure that override order is correct
109-
assertThat(ctx.getBean("overrideBean", TestBean.class).getName(), is("override-l0"));
144+
// ensure that override order is correct and that it is a singleton
145+
TestBean ob = ctx.getBean("overrideBean", TestBean.class);
146+
assertThat(ob.getName(), is("override-s1"));
147+
assertTrue(ob == ctx.getBean("overrideBean", TestBean.class));
148+
149+
TestBean pb1 = ctx.getBean("prototypeBean", TestBean.class);
150+
TestBean pb2 = ctx.getBean("prototypeBean", TestBean.class);
151+
assertTrue(pb1 != pb2);
152+
assertTrue(pb1.getFriends().iterator().next() != pb2.getFriends().iterator().next());
153+
}
154+
155+
@Test
156+
public void twoLevelsDeepWithInheritanceAndScopedProxy() {
157+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
158+
ctx.register(S1ImporterWithProxy.class);
159+
ctx.refresh();
160+
161+
S1ConfigWithProxy config = ctx.getBean(S1ConfigWithProxy.class);
162+
assertTrue(config == ctx.getBean(S1ConfigWithProxy.class));
163+
TestBean tb = ctx.getBean("l0Bean", TestBean.class);
164+
assertTrue(tb == ctx.getBean("l0Bean", TestBean.class));
165+
166+
ctx.getBean(L0Config.L1Config.class);
167+
ctx.getBean("l1Bean");
168+
169+
ctx.getBean(L0Config.L1Config.L2Config.class);
170+
ctx.getBean("l2Bean");
171+
172+
// ensure that override order is correct and that it is a singleton
173+
TestBean ob = ctx.getBean("overrideBean", TestBean.class);
174+
assertThat(ob.getName(), is("override-s1"));
175+
assertTrue(ob == ctx.getBean("overrideBean", TestBean.class));
176+
177+
TestBean pb1 = ctx.getBean("prototypeBean", TestBean.class);
178+
TestBean pb2 = ctx.getBean("prototypeBean", TestBean.class);
179+
assertTrue(pb1 != pb2);
180+
assertTrue(pb1.getFriends().iterator().next() != pb2.getFriends().iterator().next());
110181
}
111182

112183
@Test
@@ -115,21 +186,34 @@ public void twoLevelsWithNoBeanMethods() {
115186
ctx.register(L0ConfigEmpty.class);
116187
ctx.refresh();
117188

118-
ctx.getBean(L0ConfigEmpty.class);
119-
ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.class);
120-
ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.L2ConfigEmpty.class);
189+
assertFalse(ctx.getBeanFactory().containsSingleton("l0ConfigEmpty"));
190+
Object l0i1 = ctx.getBean(L0ConfigEmpty.class);
191+
Object l0i2 = ctx.getBean(L0ConfigEmpty.class);
192+
assertTrue(l0i1 == l0i2);
193+
194+
Object l1i1 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.class);
195+
Object l1i2 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.class);
196+
assertTrue(l1i1 != l1i2);
197+
198+
Object l2i1 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.L2ConfigEmpty.class);
199+
Object l2i2 = ctx.getBean(L0ConfigEmpty.L1ConfigEmpty.L2ConfigEmpty.class);
200+
assertTrue(l2i1 == l2i2);
201+
assertNotEquals(l2i1.toString(), l2i2.toString());
121202
}
122203

123204

124205
@Configuration
206+
@Lazy
125207
static class L0Config {
126208

127209
@Bean
210+
@Lazy
128211
public TestBean l0Bean() {
129212
return new TestBean("l0");
130213
}
131214

132215
@Bean
216+
@Lazy
133217
public TestBean overrideBean() {
134218
return new TestBean("override-l0");
135219
}
@@ -148,14 +232,17 @@ public TestBean overrideBean() {
148232
}
149233

150234
@Configuration
235+
@Lazy
151236
protected static class L2Config {
152237

153238
@Bean
239+
@Lazy
154240
public TestBean l2Bean() {
155241
return new TestBean("l2");
156242
}
157243

158244
@Bean
245+
@Lazy
159246
public TestBean overrideBean() {
160247
return new TestBean("override-l2");
161248
}
@@ -165,14 +252,17 @@ public TestBean overrideBean() {
165252

166253

167254
@Component
255+
@Lazy
168256
static class L0ConfigLight {
169257

170258
@Bean
259+
@Lazy
171260
public TestBean l0Bean() {
172261
return new TestBean("l0");
173262
}
174263

175264
@Bean
265+
@Lazy
176266
public TestBean overrideBean() {
177267
return new TestBean("override-l0");
178268
}
@@ -191,14 +281,17 @@ public TestBean overrideBean() {
191281
}
192282

193283
@Component
284+
@Lazy
194285
protected static class L2ConfigLight {
195286

196287
@Bean
288+
@Lazy
197289
public TestBean l2Bean() {
198290
return new TestBean("l2");
199291
}
200292

201293
@Bean
294+
@Lazy
202295
public TestBean overrideBean() {
203296
return new TestBean("override-l2");
204297
}
@@ -207,27 +300,69 @@ public TestBean overrideBean() {
207300
}
208301

209302

210-
@Component
211-
static class L0ConfigEmpty {
303+
@Configuration
304+
@Scope("prototype")
305+
static class S1Config extends L0Config {
212306

213-
@Component
214-
static class L1ConfigEmpty {
307+
@Override
308+
@Bean
309+
public TestBean overrideBean() {
310+
return new TestBean("override-s1");
311+
}
215312

216-
@Component
217-
protected static class L2ConfigEmpty {
218-
}
313+
@Bean
314+
@Scope("prototype")
315+
public TestBean prototypeBean() {
316+
TestBean tb = new TestBean("override-s1");
317+
tb.getFriends().add(this);
318+
return tb;
219319
}
220320
}
221321

222322

223323
@Configuration
224-
static class S1Config extends L0Config {
324+
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
325+
static class S1ConfigWithProxy extends L0Config {
225326

226327
@Override
227328
@Bean
228329
public TestBean overrideBean() {
229330
return new TestBean("override-s1");
230331
}
332+
333+
@Bean
334+
@Scope("prototype")
335+
public TestBean prototypeBean() {
336+
TestBean tb = new TestBean("override-s1");
337+
tb.getFriends().add(this);
338+
return tb;
339+
}
340+
}
341+
342+
343+
@Import(S1Config.class)
344+
static class S1Importer {
345+
}
346+
347+
348+
@Import(S1ConfigWithProxy.class)
349+
static class S1ImporterWithProxy {
350+
}
351+
352+
353+
@Component
354+
@Lazy
355+
static class L0ConfigEmpty {
356+
357+
@Component
358+
@Scope("prototype")
359+
static class L1ConfigEmpty {
360+
361+
@Component
362+
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
363+
protected static class L2ConfigEmpty {
364+
}
365+
}
231366
}
232367

233368
}

0 commit comments

Comments
 (0)