Skip to content

Commit d1859c8

Browse files
rwinchjhoeller
authored andcommitted
Ensure Parent ConfigurationClass loaded on overrides
Previously ConfigurationClassParser could override a nested @configuration without consideration of @bean's defined in parent classes. This commit ensures that if the original ConfigurationClass contains additional bean definitions it is processed again. Issue: SPR-10546 (cherry picked from commit 940011e)
1 parent a86283e commit d1859c8

9 files changed

+381
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
public class ImportedConfig {
29+
@Bean
30+
public String myBean() {
31+
return "myBean";
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
public class ParentConfig {
29+
@Bean
30+
public String myBean() {
31+
return "myBean";
32+
}
33+
}
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.springframework.context.annotation.ComponentScan;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.context.annotation.spr10546.scanpackage.AEnclosingConfig;
22+
23+
24+
/**
25+
*
26+
* @author Rob Winch
27+
*/
28+
@Configuration
29+
@ComponentScan(basePackageClasses=AEnclosingConfig.class)
30+
public class ParentWithComponentScanConfig {
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.Import;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
@Import(ImportedConfig.class)
29+
public class ParentWithImportConfig {
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.ImportResource;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
@ImportResource("classpath:org/springframework/context/annotation/spr10546/importedResource.xml")
29+
public class ParentWithImportResourceConfig {
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
21+
22+
/**
23+
*
24+
* @author Rob Winch
25+
*/
26+
@Configuration
27+
public class ParentWithParentConfig extends ParentConfig {
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546;
18+
19+
import org.junit.After;
20+
import org.junit.Test;
21+
import org.springframework.context.ConfigurableApplicationContext;
22+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
23+
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.context.annotation.Import;
25+
import org.springframework.context.annotation.spr10546.scanpackage.AEnclosingConfig;
26+
27+
import static org.hamcrest.CoreMatchers.*;
28+
import static org.junit.Assert.*;
29+
30+
31+
/**
32+
*
33+
* @author Rob Winch
34+
*/
35+
public class Spr10546Tests {
36+
private ConfigurableApplicationContext context;
37+
38+
@After
39+
public void closeContext() {
40+
if(context != null) {
41+
context.close();
42+
}
43+
}
44+
45+
// These fail prior to fixing SPR-10546
46+
47+
@Test
48+
public void enclosingConfigFirstParentDefinesBean() {
49+
assertLoadsMyBean(AEnclosingConfig.class,AEnclosingConfig.ChildConfig.class);
50+
}
51+
52+
/**
53+
* Prior to fixing SPR-10546 this might have succeeded depending on the ordering the
54+
* classes were picked up. If they are picked up in the same order as
55+
* {@link #enclosingConfigFirstParentDefinesBean()} then it would fail. This test is
56+
* mostly for illustration purposes, but doesn't hurt to continue using it.
57+
*
58+
* <p>We purposely use the {@link AEnclosingConfig} to make it alphabetically prior to the
59+
* {@link AEnclosingConfig.ChildConfig} which encourages this to occur with the
60+
* classpath scanning implementation being used by the author of this test.
61+
*/
62+
@Test
63+
public void enclosingConfigFirstParentDefinesBeanWithScanning() {
64+
AnnotationConfigApplicationContext ctx= new AnnotationConfigApplicationContext();
65+
context = ctx;
66+
ctx.scan(AEnclosingConfig.class.getPackage().getName());
67+
ctx.refresh();
68+
assertThat(context.getBean("myBean",String.class), equalTo("myBean"));
69+
}
70+
71+
@Test
72+
public void enclosingConfigFirstParentDefinesBeanWithImportResource() {
73+
assertLoadsMyBean(AEnclosingWithImportResourceConfig.class,AEnclosingWithImportResourceConfig.ChildConfig.class);
74+
}
75+
76+
@Configuration
77+
static class AEnclosingWithImportResourceConfig {
78+
@Configuration
79+
public static class ChildConfig extends ParentWithImportResourceConfig {}
80+
}
81+
82+
@Test
83+
public void enclosingConfigFirstParentDefinesBeanWithComponentScan() {
84+
assertLoadsMyBean(AEnclosingWithComponentScanConfig.class,AEnclosingWithComponentScanConfig.ChildConfig.class);
85+
}
86+
87+
@Configuration
88+
static class AEnclosingWithComponentScanConfig {
89+
@Configuration
90+
public static class ChildConfig extends ParentWithComponentScanConfig {}
91+
}
92+
93+
@Test
94+
public void enclosingConfigFirstParentWithParentDefinesBean() {
95+
assertLoadsMyBean(AEnclosingWithGrandparentConfig.class,AEnclosingWithGrandparentConfig.ChildConfig.class);
96+
}
97+
98+
@Configuration
99+
static class AEnclosingWithGrandparentConfig {
100+
@Configuration
101+
public static class ChildConfig extends ParentWithParentConfig {}
102+
}
103+
104+
@Test
105+
public void importChildConfigThenChildConfig() {
106+
assertLoadsMyBean(ImportChildConfig.class,ChildConfig.class);
107+
}
108+
109+
@Configuration
110+
static class ChildConfig extends ParentConfig {}
111+
112+
@Configuration
113+
@Import(ChildConfig.class)
114+
static class ImportChildConfig {}
115+
116+
117+
// These worked prior, but validating they continue to work
118+
119+
@Test
120+
public void enclosingConfigFirstParentDefinesBeanWithImport() {
121+
assertLoadsMyBean(AEnclosingWithImportConfig.class,AEnclosingWithImportConfig.ChildConfig.class);
122+
}
123+
124+
@Configuration
125+
static class AEnclosingWithImportConfig {
126+
@Configuration
127+
public static class ChildConfig extends ParentWithImportConfig {}
128+
}
129+
130+
@Test
131+
public void childConfigFirst() {
132+
assertLoadsMyBean(AEnclosingConfig.ChildConfig.class, AEnclosingConfig.class);
133+
}
134+
135+
@Test
136+
public void enclosingConfigOnly() {
137+
assertLoadsMyBean(AEnclosingConfig.class);
138+
}
139+
140+
@Test
141+
public void childConfigOnly() {
142+
assertLoadsMyBean(AEnclosingConfig.ChildConfig.class);
143+
}
144+
145+
private void assertLoadsMyBean(Class<?>... annotatedClasses) {
146+
context = new AnnotationConfigApplicationContext(annotatedClasses);
147+
assertThat(context.getBean("myBean",String.class), equalTo("myBean"));
148+
}
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:c="http://www.springframework.org/schema/c"
5+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
6+
7+
<bean id="myBean" class="java.lang.String" c:_0="myBean"/>
8+
</beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.spr10546.scanpackage;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.spr10546.ParentConfig;
21+
22+
23+
/**
24+
* Note the name of {@link AEnclosingConfig} is chosen to help ensure scanning picks up
25+
* the enclosing configuration prior to {@link ChildConfig} to demonstrate this can happen
26+
* with classpath scanning.
27+
*
28+
* @author Rob Winch
29+
*/
30+
@Configuration
31+
public class AEnclosingConfig {
32+
@Configuration
33+
public static class ChildConfig extends ParentConfig {}
34+
}

0 commit comments

Comments
 (0)