Commit 0def4477 authored by Dave Syer's avatar Dave Syer

More care required getting beans early in lifecycle

parent 0c79c891
......@@ -20,37 +20,39 @@ import org.springframework.boot.autoconfigure.condition.Outcome;
/**
* Collects details about decision made during autoconfiguration (pass or fail)
*
*
* @author Greg Turnquist
*/
public class AutoConfigurationDecision {
private final String message;
private final String classOrMethodName;
private final Outcome outcome;
public AutoConfigurationDecision(String message, String classOrMethodName, Outcome outcome) {
public AutoConfigurationDecision(String message, String classOrMethodName,
Outcome outcome) {
this.message = message;
this.classOrMethodName = classOrMethodName;
this.outcome = outcome;
}
public String getMessage() {
return message;
return this.message;
}
public String getClassOrMethodName() {
return classOrMethodName;
return this.classOrMethodName;
}
public Outcome getOutcome() {
return outcome;
return this.outcome;
}
@Override
public String toString() {
return "AutoConfigurationDecision{" + "message='" + message + '\''
+ ", classOrMethodName='" + classOrMethodName + '\'' + ", outcome="
+ outcome + '}';
return "AutoConfigurationDecision{" + "message='" + this.message + '\''
+ ", classOrMethodName='" + this.classOrMethodName + '\'' + ", outcome="
+ this.outcome + '}';
}
@Override
......@@ -62,10 +64,10 @@ public class AutoConfigurationDecision {
AutoConfigurationDecision decision = (AutoConfigurationDecision) o;
if (message != null ? !message.equals(decision.message)
if (this.message != null ? !this.message.equals(decision.message)
: decision.message != null)
return false;
if (outcome != null ? !outcome.equals(decision.outcome)
if (this.outcome != null ? !this.outcome.equals(decision.outcome)
: decision.outcome != null)
return false;
......@@ -74,8 +76,8 @@ public class AutoConfigurationDecision {
@Override
public int hashCode() {
int result = message != null ? message.hashCode() : 0;
result = 31 * result + (outcome != null ? outcome.hashCode() : 0);
int result = this.message != null ? this.message.hashCode() : 0;
result = 31 * result + (this.outcome != null ? this.outcome.hashCode() : 0);
return result;
}
}
......@@ -36,6 +36,7 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.ClassUtils;
/**
* Bean used to gather autoconfiguration decisions, and then generate a collection of info
......@@ -56,7 +57,7 @@ public class AutoConfigurationReport implements ApplicationContextAware,
private Map<String, List<AutoConfigurationDecision>> autoconfigurationDecisions = new LinkedHashMap<String, List<AutoConfigurationDecision>>();
private Map<String, List<String>> positive = new LinkedHashMap<String, List<String>>();
private Map<String, List<String>> negative = new LinkedHashMap<String, List<String>>();
private ApplicationContext context;
private ConfigurableApplicationContext context;
private boolean initialized = false;
public static void registerDecision(ConditionContext context, String message,
......@@ -64,7 +65,7 @@ public class AutoConfigurationReport implements ApplicationContextAware,
if (context.getBeanFactory().containsBeanDefinition(AUTO_CONFIGURATION_REPORT)
|| context.getBeanFactory().containsSingleton(AUTO_CONFIGURATION_REPORT)) {
AutoConfigurationReport autoconfigurationReport = context.getBeanFactory()
.getBean(AutoConfigurationReport.class);
.getBean(AUTO_CONFIGURATION_REPORT, AutoConfigurationReport.class);
autoconfigurationReport.registerDecision(message, classOrMethodName, outcome);
}
}
......@@ -120,7 +121,7 @@ public class AutoConfigurationReport implements ApplicationContextAware,
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
this.context = (ConfigurableApplicationContext) applicationContext;
}
@Override
......@@ -133,17 +134,20 @@ public class AutoConfigurationReport implements ApplicationContextAware,
synchronized (this) {
if (!this.initialized) {
this.initialized = true;
splitDecisionsIntoPositiveAndNegative();
scanPositiveDecisionsForBeansBootCreated();
if (this.context.getEnvironment().getProperty("debug", Boolean.class,
false)) {
logger.info("Created beans:");
for (CreatedBeanInfo info : this.beansCreated) {
logger.info(info);
}
logger.info("Negative decisions:");
for (String key : this.negative.keySet()) {
logger.info(key + ": " + this.negative.get(key));
try {
splitDecisionsIntoPositiveAndNegative();
scanPositiveDecisionsForBeansBootCreated();
}
finally {
if (shouldLogReport()) {
logger.info("Created beans:");
for (CreatedBeanInfo info : this.beansCreated) {
logger.info(info);
}
logger.info("Negative decisions:");
for (String key : this.negative.keySet()) {
logger.info(key + ": " + this.negative.get(key));
}
}
}
}
......@@ -151,6 +155,11 @@ public class AutoConfigurationReport implements ApplicationContextAware,
}
}
private boolean shouldLogReport() {
return this.context.getEnvironment().getProperty("debug", Boolean.class, false)
|| !this.context.isActive();
}
/**
* Scan the list of {@link AutoConfigurationDecision}'s, and if all outcomes true,
* then put it on the positive list. Otherwise, put it on the negative list.
......@@ -194,21 +203,36 @@ public class AutoConfigurationReport implements ApplicationContextAware,
for (AutoConfigurationDecision decision : this.autoconfigurationDecisions
.get(key)) {
for (String beanName : this.context.getBeanDefinitionNames()) {
Object bean = this.context.getBean(beanName);
Object bean = null;
if (decision.getMessage().contains(beanName)
&& decision.getMessage().contains("matched")) {
boolean anyMethodsAreBeans = false;
for (Method method : bean.getClass().getMethods()) {
if (this.context.containsBean(method.getName())) {
this.beansCreated.add(new CreatedBeanInfo(method
.getName(), method.getReturnType(), this.positive
.get(key)));
anyMethodsAreBeans = true;
try {
bean = this.context.getBean(beanName);
boolean anyMethodsAreBeans = false;
for (Method method : bean.getClass().getMethods()) {
if (this.context.containsBean(method.getName())) {
this.beansCreated.add(new CreatedBeanInfo(method
.getName(), method.getReturnType(),
this.positive.get(key)));
anyMethodsAreBeans = true;
}
}
}
if (!anyMethodsAreBeans) {
this.beansCreated.add(new CreatedBeanInfo(beanName, bean,
if (!anyMethodsAreBeans) {
this.beansCreated.add(new CreatedBeanInfo(beanName, bean
.getClass(), this.positive.get(key)));
}
}
catch (RuntimeException e) {
Class<?> type = null;
ConfigurableApplicationContext configurable = this.context;
String beanClassName = configurable.getBeanFactory()
.getBeanDefinition(beanName).getBeanClassName();
if (beanClassName != null) {
type = ClassUtils.resolveClassName(beanClassName,
configurable.getClassLoader());
}
this.beansCreated.add(new CreatedBeanInfo(beanName, type,
this.positive.get(key)));
}
}
......
......@@ -29,12 +29,6 @@ public class CreatedBeanInfo {
private final Class<?> type;
private final List<String> decisions;
public CreatedBeanInfo(String beanName, Object bean, List<String> decisions) {
this.name = beanName;
this.type = bean.getClass();
this.decisions = decisions;
}
public CreatedBeanInfo(String beanName, Class<?> declaredBeanType,
List<String> decisions) {
this.name = beanName;
......
......@@ -168,7 +168,7 @@ public class SpringApplication {
private boolean webEnvironment;
private List<ApplicationContextInitializer<?>> initializers;
private Collection<ApplicationContextInitializer<?>> initializers;
private Map<String, Object> defaultProperties;
......@@ -207,7 +207,7 @@ public class SpringApplication {
this.initialSources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers = new LinkedHashSet<ApplicationContextInitializer<?>>();
this.initializers.addAll(getSpringFactoriesApplicationContextInitializers());
this.mainApplicationClass = deduceMainApplicationClass();
}
......@@ -716,15 +716,12 @@ public class SpringApplication {
}
/**
* Returns a mutable list of the {@link ApplicationContextInitializer}s that will be
* Returns readonly list of the {@link ApplicationContextInitializer}s that will be
* applied to the Spring {@link ApplicationContext}.
* @return the initializers
*/
public List<ApplicationContextInitializer<?>> getInitializers() {
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
getSpringFactoriesApplicationContextInitializers());
initializers.addAll(this.initializers);
return initializers;
return new ArrayList<ApplicationContextInitializer<?>>(this.initializers);
}
/**
......
......@@ -70,6 +70,7 @@ public class SpringApplicationBuilder {
private Map<String, Object> defaultProperties = new LinkedHashMap<String, Object>();
private ConfigurableEnvironment environment;
private Set<String> additionalProfiles = new LinkedHashSet<String>();
private Set<ApplicationContextInitializer<?>> initializers = new LinkedHashSet<ApplicationContextInitializer<?>>();
public SpringApplicationBuilder(Object... sources) {
this.application = new SpringApplication(sources);
......@@ -466,12 +467,13 @@ public class SpringApplicationBuilder {
Set<ApplicationContextInitializer<?>> target = new LinkedHashSet<ApplicationContextInitializer<?>>();
if (prepend) {
target.addAll(Arrays.asList(initializers));
target.addAll(this.application.getInitializers());
target.addAll(this.initializers);
}
else {
target.addAll(this.application.getInitializers());
target.addAll(this.initializers);
target.addAll(Arrays.asList(initializers));
}
this.initializers = target;
this.application.setInitializers(target);
}
......
......@@ -34,6 +34,7 @@ import org.springframework.util.StringUtils;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.spy;
......@@ -169,6 +170,14 @@ public class SpringApplicationBuilderTests {
any(ApplicationContext.class));
}
@Test
public void initializersCreatedOnce() throws Exception {
SpringApplicationBuilder application = new SpringApplicationBuilder(
ExampleConfig.class).web(false);
this.context = application.run();
assertEquals(7, application.application().getInitializers().size());
}
@Configuration
static class ExampleConfig {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment