Skip to content

Commit 0def447

Browse files
author
Dave Syer
committed
More care required getting beans early in lifecycle
1 parent 0c79c89 commit 0def447

File tree

6 files changed

+80
-52
lines changed

6 files changed

+80
-52
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/report/AutoConfigurationDecision.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,39 @@
2020

2121
/**
2222
* Collects details about decision made during autoconfiguration (pass or fail)
23-
*
23+
*
2424
* @author Greg Turnquist
2525
*/
2626
public class AutoConfigurationDecision {
27+
2728
private final String message;
2829
private final String classOrMethodName;
2930
private final Outcome outcome;
3031

31-
public AutoConfigurationDecision(String message, String classOrMethodName, Outcome outcome) {
32+
public AutoConfigurationDecision(String message, String classOrMethodName,
33+
Outcome outcome) {
3234
this.message = message;
3335
this.classOrMethodName = classOrMethodName;
3436
this.outcome = outcome;
3537
}
3638

3739
public String getMessage() {
38-
return message;
40+
return this.message;
3941
}
4042

4143
public String getClassOrMethodName() {
42-
return classOrMethodName;
44+
return this.classOrMethodName;
4345
}
4446

4547
public Outcome getOutcome() {
46-
return outcome;
48+
return this.outcome;
4749
}
4850

4951
@Override
5052
public String toString() {
51-
return "AutoConfigurationDecision{" + "message='" + message + '\''
52-
+ ", classOrMethodName='" + classOrMethodName + '\'' + ", outcome="
53-
+ outcome + '}';
53+
return "AutoConfigurationDecision{" + "message='" + this.message + '\''
54+
+ ", classOrMethodName='" + this.classOrMethodName + '\'' + ", outcome="
55+
+ this.outcome + '}';
5456
}
5557

5658
@Override
@@ -62,10 +64,10 @@ public boolean equals(Object o) {
6264

6365
AutoConfigurationDecision decision = (AutoConfigurationDecision) o;
6466

65-
if (message != null ? !message.equals(decision.message)
67+
if (this.message != null ? !this.message.equals(decision.message)
6668
: decision.message != null)
6769
return false;
68-
if (outcome != null ? !outcome.equals(decision.outcome)
70+
if (this.outcome != null ? !this.outcome.equals(decision.outcome)
6971
: decision.outcome != null)
7072
return false;
7173

@@ -74,8 +76,8 @@ public boolean equals(Object o) {
7476

7577
@Override
7678
public int hashCode() {
77-
int result = message != null ? message.hashCode() : 0;
78-
result = 31 * result + (outcome != null ? outcome.hashCode() : 0);
79+
int result = this.message != null ? this.message.hashCode() : 0;
80+
result = 31 * result + (this.outcome != null ? this.outcome.hashCode() : 0);
7981
return result;
8082
}
8183
}

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/report/AutoConfigurationReport.java

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.context.ConfigurableApplicationContext;
3737
import org.springframework.context.annotation.ConditionContext;
3838
import org.springframework.context.event.ContextRefreshedEvent;
39+
import org.springframework.util.ClassUtils;
3940

4041
/**
4142
* Bean used to gather autoconfiguration decisions, and then generate a collection of info
@@ -56,15 +57,15 @@ public class AutoConfigurationReport implements ApplicationContextAware,
5657
private Map<String, List<AutoConfigurationDecision>> autoconfigurationDecisions = new LinkedHashMap<String, List<AutoConfigurationDecision>>();
5758
private Map<String, List<String>> positive = new LinkedHashMap<String, List<String>>();
5859
private Map<String, List<String>> negative = new LinkedHashMap<String, List<String>>();
59-
private ApplicationContext context;
60+
private ConfigurableApplicationContext context;
6061
private boolean initialized = false;
6162

6263
public static void registerDecision(ConditionContext context, String message,
6364
String classOrMethodName, Outcome outcome) {
6465
if (context.getBeanFactory().containsBeanDefinition(AUTO_CONFIGURATION_REPORT)
6566
|| context.getBeanFactory().containsSingleton(AUTO_CONFIGURATION_REPORT)) {
6667
AutoConfigurationReport autoconfigurationReport = context.getBeanFactory()
67-
.getBean(AutoConfigurationReport.class);
68+
.getBean(AUTO_CONFIGURATION_REPORT, AutoConfigurationReport.class);
6869
autoconfigurationReport.registerDecision(message, classOrMethodName, outcome);
6970
}
7071
}
@@ -120,7 +121,7 @@ public Set<String> getBeanNamesCreated() {
120121
@Override
121122
public void setApplicationContext(ApplicationContext applicationContext)
122123
throws BeansException {
123-
this.context = applicationContext;
124+
this.context = (ConfigurableApplicationContext) applicationContext;
124125
}
125126

126127
@Override
@@ -133,24 +134,32 @@ public void initialize() {
133134
synchronized (this) {
134135
if (!this.initialized) {
135136
this.initialized = true;
136-
splitDecisionsIntoPositiveAndNegative();
137-
scanPositiveDecisionsForBeansBootCreated();
138-
if (this.context.getEnvironment().getProperty("debug", Boolean.class,
139-
false)) {
140-
logger.info("Created beans:");
141-
for (CreatedBeanInfo info : this.beansCreated) {
142-
logger.info(info);
143-
}
144-
logger.info("Negative decisions:");
145-
for (String key : this.negative.keySet()) {
146-
logger.info(key + ": " + this.negative.get(key));
137+
try {
138+
splitDecisionsIntoPositiveAndNegative();
139+
scanPositiveDecisionsForBeansBootCreated();
140+
}
141+
finally {
142+
if (shouldLogReport()) {
143+
logger.info("Created beans:");
144+
for (CreatedBeanInfo info : this.beansCreated) {
145+
logger.info(info);
146+
}
147+
logger.info("Negative decisions:");
148+
for (String key : this.negative.keySet()) {
149+
logger.info(key + ": " + this.negative.get(key));
150+
}
147151
}
148152
}
149153
}
150154
}
151155
}
152156
}
153157

158+
private boolean shouldLogReport() {
159+
return this.context.getEnvironment().getProperty("debug", Boolean.class, false)
160+
|| !this.context.isActive();
161+
}
162+
154163
/**
155164
* Scan the list of {@link AutoConfigurationDecision}'s, and if all outcomes true,
156165
* then put it on the positive list. Otherwise, put it on the negative list.
@@ -194,21 +203,36 @@ private synchronized void scanPositiveDecisionsForBeansBootCreated() {
194203
for (AutoConfigurationDecision decision : this.autoconfigurationDecisions
195204
.get(key)) {
196205
for (String beanName : this.context.getBeanDefinitionNames()) {
197-
Object bean = this.context.getBean(beanName);
206+
Object bean = null;
198207
if (decision.getMessage().contains(beanName)
199208
&& decision.getMessage().contains("matched")) {
200-
boolean anyMethodsAreBeans = false;
201-
for (Method method : bean.getClass().getMethods()) {
202-
if (this.context.containsBean(method.getName())) {
203-
this.beansCreated.add(new CreatedBeanInfo(method
204-
.getName(), method.getReturnType(), this.positive
205-
.get(key)));
206-
anyMethodsAreBeans = true;
209+
try {
210+
bean = this.context.getBean(beanName);
211+
boolean anyMethodsAreBeans = false;
212+
for (Method method : bean.getClass().getMethods()) {
213+
if (this.context.containsBean(method.getName())) {
214+
this.beansCreated.add(new CreatedBeanInfo(method
215+
.getName(), method.getReturnType(),
216+
this.positive.get(key)));
217+
anyMethodsAreBeans = true;
218+
}
207219
}
208-
}
209220

210-
if (!anyMethodsAreBeans) {
211-
this.beansCreated.add(new CreatedBeanInfo(beanName, bean,
221+
if (!anyMethodsAreBeans) {
222+
this.beansCreated.add(new CreatedBeanInfo(beanName, bean
223+
.getClass(), this.positive.get(key)));
224+
}
225+
}
226+
catch (RuntimeException e) {
227+
Class<?> type = null;
228+
ConfigurableApplicationContext configurable = this.context;
229+
String beanClassName = configurable.getBeanFactory()
230+
.getBeanDefinition(beanName).getBeanClassName();
231+
if (beanClassName != null) {
232+
type = ClassUtils.resolveClassName(beanClassName,
233+
configurable.getClassLoader());
234+
}
235+
this.beansCreated.add(new CreatedBeanInfo(beanName, type,
212236
this.positive.get(key)));
213237
}
214238
}

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/report/CreatedBeanInfo.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ public class CreatedBeanInfo {
2929
private final Class<?> type;
3030
private final List<String> decisions;
3131

32-
public CreatedBeanInfo(String beanName, Object bean, List<String> decisions) {
33-
this.name = beanName;
34-
this.type = bean.getClass();
35-
this.decisions = decisions;
36-
}
37-
3832
public CreatedBeanInfo(String beanName, Class<?> declaredBeanType,
3933
List<String> decisions) {
4034
this.name = beanName;

spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public class SpringApplication {
168168

169169
private boolean webEnvironment;
170170

171-
private List<ApplicationContextInitializer<?>> initializers;
171+
private Collection<ApplicationContextInitializer<?>> initializers;
172172

173173
private Map<String, Object> defaultProperties;
174174

@@ -207,7 +207,7 @@ private void initialize(Object[] sources) {
207207
this.initialSources.addAll(Arrays.asList(sources));
208208
}
209209
this.webEnvironment = deduceWebEnvironment();
210-
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
210+
this.initializers = new LinkedHashSet<ApplicationContextInitializer<?>>();
211211
this.initializers.addAll(getSpringFactoriesApplicationContextInitializers());
212212
this.mainApplicationClass = deduceMainApplicationClass();
213213
}
@@ -716,15 +716,12 @@ public void addInitializers(ApplicationContextInitializer<?>... initializers) {
716716
}
717717

718718
/**
719-
* Returns a mutable list of the {@link ApplicationContextInitializer}s that will be
719+
* Returns readonly list of the {@link ApplicationContextInitializer}s that will be
720720
* applied to the Spring {@link ApplicationContext}.
721721
* @return the initializers
722722
*/
723723
public List<ApplicationContextInitializer<?>> getInitializers() {
724-
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
725-
getSpringFactoriesApplicationContextInitializers());
726-
initializers.addAll(this.initializers);
727-
return initializers;
724+
return new ArrayList<ApplicationContextInitializer<?>>(this.initializers);
728725
}
729726

730727
/**

spring-boot/src/main/java/org/springframework/boot/builder/SpringApplicationBuilder.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class SpringApplicationBuilder {
7070
private Map<String, Object> defaultProperties = new LinkedHashMap<String, Object>();
7171
private ConfigurableEnvironment environment;
7272
private Set<String> additionalProfiles = new LinkedHashSet<String>();
73+
private Set<ApplicationContextInitializer<?>> initializers = new LinkedHashSet<ApplicationContextInitializer<?>>();
7374

7475
public SpringApplicationBuilder(Object... sources) {
7576
this.application = new SpringApplication(sources);
@@ -466,12 +467,13 @@ private void addInitializers(boolean prepend,
466467
Set<ApplicationContextInitializer<?>> target = new LinkedHashSet<ApplicationContextInitializer<?>>();
467468
if (prepend) {
468469
target.addAll(Arrays.asList(initializers));
469-
target.addAll(this.application.getInitializers());
470+
target.addAll(this.initializers);
470471
}
471472
else {
472-
target.addAll(this.application.getInitializers());
473+
target.addAll(this.initializers);
473474
target.addAll(Arrays.asList(initializers));
474475
}
476+
this.initializers = target;
475477
this.application.setInitializers(target);
476478
}
477479

spring-boot/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static org.hamcrest.Matchers.equalTo;
3535
import static org.hamcrest.Matchers.instanceOf;
3636
import static org.hamcrest.Matchers.is;
37+
import static org.junit.Assert.assertEquals;
3738
import static org.junit.Assert.assertThat;
3839
import static org.mockito.Matchers.any;
3940
import static org.mockito.Mockito.spy;
@@ -169,6 +170,14 @@ public void parentContextIdentical() throws Exception {
169170
any(ApplicationContext.class));
170171
}
171172

173+
@Test
174+
public void initializersCreatedOnce() throws Exception {
175+
SpringApplicationBuilder application = new SpringApplicationBuilder(
176+
ExampleConfig.class).web(false);
177+
this.context = application.run();
178+
assertEquals(7, application.application().getInitializers().size());
179+
}
180+
172181
@Configuration
173182
static class ExampleConfig {
174183

0 commit comments

Comments
 (0)