More care required getting beans early in lifecycle
This commit is contained in:
@@ -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
|
||||
.getClass(), this.positive.get(key)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyMethodsAreBeans) {
|
||||
this.beansCreated.add(new CreatedBeanInfo(beanName, bean,
|
||||
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 {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user