mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
ApplicationContextInitializers now listen for ContextRefreshedEvent
The AutoConfigurationReportLoggingInitializer wasn't working in non-GenericApplicationContext becasue teh BeanFatcory wasn't available for registering its listener during initialization. Instead of relying on that rather fragile state I decided to give any ApplicationContextInitializer that was itself an ApplicationListener an explicit callback with a ContextRefreshedEvent, and move that interface up a level in the logging initializer. Works much better.
This commit is contained in:
parent
f3a225f35f
commit
285dd5b270
@ -20,7 +20,6 @@ import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringApplicationErrorHandler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationReport.ConditionAndOutcome;
|
||||
@ -30,6 +29,7 @@ import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@ -49,18 +49,21 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class AutoConfigurationReportLoggingInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>,
|
||||
SpringApplicationErrorHandler {
|
||||
SpringApplicationErrorHandler, ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private static final String LOGGER_BEAN = "autoConfigurationReportLogger";
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private AutoConfigurationReportLogger loggerBean;
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
private AutoConfigurationReport report;
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
this.loggerBean = new AutoConfigurationReportLogger(applicationContext);
|
||||
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
|
||||
if (!beanFactory.containsSingleton(LOGGER_BEAN)) {
|
||||
beanFactory.registerSingleton(LOGGER_BEAN, this.loggerBean);
|
||||
this.applicationContext = applicationContext;
|
||||
if (applicationContext instanceof GenericApplicationContext) {
|
||||
// Get the report early in case the context fails to load
|
||||
this.report = AutoConfigurationReport.get(this.applicationContext
|
||||
.getBeanFactory());
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,99 +71,80 @@ public class AutoConfigurationReportLoggingInitializer implements
|
||||
public void handleError(SpringApplication application,
|
||||
ConfigurableApplicationContext applicationContext, String[] args,
|
||||
Throwable exception) {
|
||||
if (this.loggerBean != null) {
|
||||
this.loggerBean.logAutoConfigurationReport(true);
|
||||
logAutoConfigurationReport(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (event.getApplicationContext() == this.applicationContext) {
|
||||
logAutoConfigurationReport();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring bean to actually perform the logging.
|
||||
*/
|
||||
public static class AutoConfigurationReportLogger implements
|
||||
ApplicationListener<ContextRefreshedEvent> {
|
||||
private void logAutoConfigurationReport() {
|
||||
logAutoConfigurationReport(!this.applicationContext.isActive());
|
||||
}
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ConfigurableApplicationContext applicationContext;
|
||||
|
||||
private final AutoConfigurationReport report;
|
||||
|
||||
public AutoConfigurationReportLogger(
|
||||
ConfigurableApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
// Get the report early in case the context fails to load
|
||||
void logAutoConfigurationReport(boolean isCrashReport) {
|
||||
if (this.report == null) {
|
||||
this.report = AutoConfigurationReport.get(this.applicationContext
|
||||
.getBeanFactory());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (event.getApplicationContext() == this.applicationContext) {
|
||||
logAutoConfigurationReport();
|
||||
if (this.report.getConditionAndOutcomesBySource().size() > 0) {
|
||||
if (isCrashReport && this.logger.isInfoEnabled()) {
|
||||
this.logger.info(getLogMessage(this.report
|
||||
.getConditionAndOutcomesBySource()));
|
||||
}
|
||||
else if (!isCrashReport && this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(getLogMessage(this.report
|
||||
.getConditionAndOutcomesBySource()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logAutoConfigurationReport() {
|
||||
logAutoConfigurationReport(!this.applicationContext.isActive());
|
||||
}
|
||||
|
||||
void logAutoConfigurationReport(boolean isCrashReport) {
|
||||
if (this.report.getConditionAndOutcomesBySource().size() > 0) {
|
||||
if (isCrashReport && this.logger.isInfoEnabled()) {
|
||||
this.logger.info(getLogMessage(this.report
|
||||
.getConditionAndOutcomesBySource()));
|
||||
}
|
||||
else if (!isCrashReport && this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(getLogMessage(this.report
|
||||
.getConditionAndOutcomesBySource()));
|
||||
}
|
||||
private StringBuilder getLogMessage(Map<String, ConditionAndOutcomes> outcomes) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append("\n\n\n");
|
||||
message.append("=========================\n");
|
||||
message.append("AUTO-CONFIGURATION REPORT\n");
|
||||
message.append("=========================\n\n\n");
|
||||
message.append("Positive matches:\n");
|
||||
message.append("-----------------\n");
|
||||
for (Map.Entry<String, ConditionAndOutcomes> entry : outcomes.entrySet()) {
|
||||
if (entry.getValue().isFullMatch()) {
|
||||
addLogMessage(message, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuilder getLogMessage(Map<String, ConditionAndOutcomes> outcomes) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append("\n\n\n");
|
||||
message.append("=========================\n");
|
||||
message.append("AUTO-CONFIGURATION REPORT\n");
|
||||
message.append("=========================\n\n\n");
|
||||
message.append("Positive matches:\n");
|
||||
message.append("-----------------\n");
|
||||
for (Map.Entry<String, ConditionAndOutcomes> entry : outcomes.entrySet()) {
|
||||
if (entry.getValue().isFullMatch()) {
|
||||
addLogMessage(message, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
message.append("\n\n");
|
||||
message.append("Negative matches:\n");
|
||||
message.append("-----------------\n");
|
||||
for (Map.Entry<String, ConditionAndOutcomes> entry : outcomes.entrySet()) {
|
||||
if (!entry.getValue().isFullMatch()) {
|
||||
addLogMessage(message, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
message.append("\n\n");
|
||||
return message;
|
||||
}
|
||||
|
||||
private void addLogMessage(StringBuilder message, String source,
|
||||
ConditionAndOutcomes conditionAndOutcomes) {
|
||||
message.append("\n " + ClassUtils.getShortName(source) + "\n");
|
||||
for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
|
||||
message.append(" - ");
|
||||
if (StringUtils.hasLength(conditionAndOutcome.getOutcome().getMessage())) {
|
||||
message.append(conditionAndOutcome.getOutcome().getMessage());
|
||||
}
|
||||
else {
|
||||
message.append(conditionAndOutcome.getOutcome().isMatch() ? "matched"
|
||||
: "did not match");
|
||||
}
|
||||
message.append(" (");
|
||||
message.append(ClassUtils.getShortName(conditionAndOutcome.getCondition()
|
||||
.getClass()));
|
||||
message.append(")\n");
|
||||
message.append("\n\n");
|
||||
message.append("Negative matches:\n");
|
||||
message.append("-----------------\n");
|
||||
for (Map.Entry<String, ConditionAndOutcomes> entry : outcomes.entrySet()) {
|
||||
if (!entry.getValue().isFullMatch()) {
|
||||
addLogMessage(message, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
message.append("\n\n");
|
||||
return message;
|
||||
}
|
||||
|
||||
private void addLogMessage(StringBuilder message, String source,
|
||||
ConditionAndOutcomes conditionAndOutcomes) {
|
||||
message.append("\n " + ClassUtils.getShortName(source) + "\n");
|
||||
for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
|
||||
message.append(" - ");
|
||||
if (StringUtils.hasLength(conditionAndOutcome.getOutcome().getMessage())) {
|
||||
message.append(conditionAndOutcome.getOutcome().getMessage());
|
||||
}
|
||||
else {
|
||||
message.append(conditionAndOutcome.getOutcome().isMatch() ? "matched"
|
||||
: "did not match");
|
||||
}
|
||||
message.append(" (");
|
||||
message.append(ClassUtils.getShortName(conditionAndOutcome.getCondition()
|
||||
.getClass()));
|
||||
message.append(")\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,16 +29,19 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationReportLoggingInitializer.AutoConfigurationReportLogger;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@ -104,6 +107,7 @@ public class AutoConfigurationReportLoggingInitializerTests {
|
||||
this.initializer.initialize(context);
|
||||
context.register(Config.class);
|
||||
context.refresh();
|
||||
this.initializer.onApplicationEvent(new ContextRefreshedEvent(context));
|
||||
assertThat(this.debugLog.size(), not(equalTo(0)));
|
||||
}
|
||||
|
||||
@ -130,6 +134,7 @@ public class AutoConfigurationReportLoggingInitializerTests {
|
||||
this.initializer.initialize(context);
|
||||
context.register(Config.class);
|
||||
context.refresh();
|
||||
this.initializer.onApplicationEvent(new ContextRefreshedEvent(context));
|
||||
for (String message : this.debugLog) {
|
||||
System.out.println(message);
|
||||
}
|
||||
@ -138,10 +143,29 @@ public class AutoConfigurationReportLoggingInitializerTests {
|
||||
assertThat(l, containsString("not a web application (OnWebApplicationCondition)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canBeUsedInApplicationContext() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(Config.class);
|
||||
new AutoConfigurationReportLoggingInitializer().initialize(context);
|
||||
context.refresh();
|
||||
assertNotNull(context.getBean(AutoConfigurationReport.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canBeUsedInNonGenericApplicationContext() throws Exception {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.register(Config.class);
|
||||
new AutoConfigurationReportLoggingInitializer().initialize(context);
|
||||
context.refresh();
|
||||
assertNotNull(context.getBean(AutoConfigurationReport.class));
|
||||
}
|
||||
|
||||
public static class MockLogFactory extends LogFactoryImpl {
|
||||
@Override
|
||||
public Log getInstance(String name) throws LogConfigurationException {
|
||||
if (AutoConfigurationReportLogger.class.getName().equals(name)) {
|
||||
if (AutoConfigurationReportLoggingInitializer.class.getName().equals(name)) {
|
||||
return logThreadLocal.get();
|
||||
}
|
||||
return new NoOpLog();
|
||||
|
@ -36,12 +36,16 @@ import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
@ -298,7 +302,7 @@ public class SpringApplication {
|
||||
getApplicationLog(), stopWatch);
|
||||
}
|
||||
|
||||
runCommandLineRunners(context, args);
|
||||
afterRefresh(context, args);
|
||||
return context;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
@ -312,6 +316,19 @@ public class SpringApplication {
|
||||
|
||||
}
|
||||
|
||||
private void afterRefresh(ConfigurableApplicationContext context, String[] args) {
|
||||
ApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
|
||||
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
|
||||
getInitializers());
|
||||
for (ApplicationContextInitializer<?> initializer : initializers) {
|
||||
if (initializer instanceof ApplicationListener) {
|
||||
multicaster.addApplicationListener((ApplicationListener<?>) initializer);
|
||||
}
|
||||
}
|
||||
multicaster.multicastEvent(new ContextRefreshedEvent(context));
|
||||
runCommandLineRunners(context, args);
|
||||
}
|
||||
|
||||
private void handleError(ConfigurableApplicationContext context, String[] args,
|
||||
Throwable exception) {
|
||||
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(
|
||||
|
@ -33,11 +33,13 @@ import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletConta
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.CommandLinePropertySource;
|
||||
@ -156,6 +158,30 @@ public class SpringApplicationTests {
|
||||
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextRefreshedEventListener() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
final AtomicReference<ApplicationContext> reference = new AtomicReference<ApplicationContext>();
|
||||
class InitalizerListener implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>,
|
||||
ApplicationListener<ContextRefreshedEvent> {
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
reference.set(event.getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
}
|
||||
}
|
||||
application.setInitializers(Arrays.asList(new InitalizerListener()));
|
||||
this.context = application.run("--foo=bar");
|
||||
assertThat(this.context, sameInstance(reference.get()));
|
||||
// Custom initializers do not switch off the defaults
|
||||
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultApplicationContext() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
|
Loading…
Reference in New Issue
Block a user