Springboot內建ApplicationContextInitializer--ConditionEvaluationReportLoggingListener
阿新 • • 發佈:2018-12-03
原始碼分析
本文程式碼基於 Springboot 2.1.0
package org.springframework.boot.autoconfigure.logging;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework. boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.logging.LogLevel;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework. context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.Ordered;
import org.springframework. core.ResolvableType;
import org.springframework.util.Assert;
/**
* ApplicationContextInitializer that writes the ConditionEvaluationReport
* to the log. Reports are logged at the DEBUG level. A crash
* report triggers an info output suggesting the user runs again with debug enabled to
* display the report.
* 雖然名字是一個Listener,但這實際上是一個ApplicationContextInitializer,其目的是將
* ConditionEvaluationReport寫入到日誌,使用DEBUG級別輸出。程式崩潰報告會觸發一個訊息輸出,
* 建議使用者使用除錯模式顯示報告。
*
* 該ApplicationContextInitializer的具體做法是在應用初始化時繫結一個ConditionEvaluationReportListener
* 事件監聽器,然後相應的事件發生時輸出ConditionEvaluationReport報告。
*
* This initializer is not intended to be shared across multiple application context
* instances.
*
* @author Greg Turnquist
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Madhura Bhave
*/
public class ConditionEvaluationReportLoggingListener
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final Log logger = LogFactory.getLog(getClass());
private ConfigurableApplicationContext applicationContext;
private ConditionEvaluationReport report;
private final LogLevel logLevelForReport;
public ConditionEvaluationReportLoggingListener() {
this(LogLevel.DEBUG);
}
public ConditionEvaluationReportLoggingListener(LogLevel logLevelForReport) {
Assert.isTrue(isInfoOrDebug(logLevelForReport), "LogLevel must be INFO or DEBUG");
this.logLevelForReport = logLevelForReport;
}
private boolean isInfoOrDebug(LogLevel logLevelForReport) {
return LogLevel.INFO.equals(logLevelForReport)
|| LogLevel.DEBUG.equals(logLevelForReport);
}
public LogLevel getLogLevelForReport() {
return this.logLevelForReport;
}
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
// 向容器繫結一個ConditionEvaluationReportListener事件監聽器
applicationContext
.addApplicationListener(new ConditionEvaluationReportListener());
if (applicationContext instanceof GenericApplicationContext) {
// Get the report early in case the context fails to load
this.report = ConditionEvaluationReport
.get(this.applicationContext.getBeanFactory());
}
}
protected void onApplicationEvent(ApplicationEvent event) {
ConfigurableApplicationContext initializerApplicationContext = this.applicationContext;
if (event instanceof ContextRefreshedEvent) {
// 遇到ContextRefreshedEvent事件,根據繫結應用上下文是否活躍作相應報告
if (((ApplicationContextEvent) event)
.getApplicationContext() == initializerApplicationContext) {
// 事件應用上下文是該初始化器所繫結的應用上下文的話才報告
logAutoConfigurationReport();
}
}
else if (event instanceof ApplicationFailedEvent
&& ((ApplicationFailedEvent) event)
.getApplicationContext() == initializerApplicationContext) {
// 遇到ApplicationFailedEvent事件,說起應用程式啟動失敗,做應用崩潰報告
// 事件應用上下文是該初始化器所繫結的應用上下文的話才報告
logAutoConfigurationReport(true);
}
}
private void logAutoConfigurationReport() {
// 如果所繫結應用上下文不活躍則認為是崩潰,否則認為是正常
logAutoConfigurationReport(!this.applicationContext.isActive());
}
public void logAutoConfigurationReport(boolean isCrashReport) {
if (this.report == null) {
if (this.applicationContext == null) {
this.logger.info("Unable to provide the conditions report "
+ "due to missing ApplicationContext");
return;
}
this.report = ConditionEvaluationReport
.get(this.applicationContext.getBeanFactory());
}
if (!this.report.getConditionAndOutcomesBySource().isEmpty()) {
if (this.getLogLevelForReport().equals(LogLevel.INFO)) {
if (this.logger.isInfoEnabled()) {
this.logger.info(new ConditionEvaluationReportMessage(this.report));
}
else if (isCrashReport) {
logMessage("info");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug(new ConditionEvaluationReportMessage(this.report));
}
else if (isCrashReport) {
logMessage("debug");
}
}
}
}
private void logMessage(String logLevel) {
this.logger.info(
String.format("%n%nError starting ApplicationContext. To display the "
+ "conditions report re-run your application with '" + logLevel
+ "' enabled."));
}
private class ConditionEvaluationReportListener
implements GenericApplicationListener {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
Class<?> type = resolvableType.getRawClass();
if (type == null) {
return false;
}
return ContextRefreshedEvent.class.isAssignableFrom(type)
|| ApplicationFailedEvent.class.isAssignableFrom(type);
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return true;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
ConditionEvaluationReportLoggingListener.this.onApplicationEvent(event);
}
}
}