诊断分析模块简介
springboot在启动过程中,会对启动失败的原因及异常进行诊断,并打印报告
工作流程
初始化阶段:加载springboot内置配置的分析器 准备阶段:设置bean工厂/环境配置 分析阶段:遍历调用分析器,只要有分析结果返回 报告阶段:将分析结果进行整理格式化,方便用户解读
源码分析
诊断分析模块主要分为:分析器、报告呈现
分析入口类
入口类:FailureAnalyzers 构造器
public FailureAnalyzers(ConfigurableApplicationContext context) {
this(context, null);
}
FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
Assert.notNull(context, "Context must not be null");
this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader);
// 初始化分析器
this.analyzers = loadFailureAnalyzers(this.classLoader);
prepareFailureAnalyzers(this.analyzers, context);
}
// 从spring.factory文件中读取分析器
private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
List<String> analyzerNames = SpringFactoriesLoader
.loadFactoryNames(FailureAnalyzer.class, classLoader);
List<FailureAnalyzer> analyzers = new ArrayList<FailureAnalyzer>();
for (String analyzerName : analyzerNames) {
try {
Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader)
.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
analyzers.add((FailureAnalyzer) constructor.newInstance());
}
catch (Throwable ex) {
logger.trace("Failed to load " + analyzerName, ex);
}
}
AnnotationAwareOrderComparator.sort(analyzers);
return analyzers;
}
分析报告核心方法
public boolean analyzeAndReport(Throwable failure) {
// 先分析
FailureAnalysis analysis = analyze(failure, this.analyzers);
// 再生成报告
return report(analysis, this.classLoader);
}
private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
// 遍历调用每个分析器分析方法
for (FailureAnalyzer analyzer : analyzers) {
try {
FailureAnalysis analysis = analyzer.analyze(failure);
if (analysis != null) {
return analysis;
}
}
catch (Throwable ex) {
logger.debug("FailureAnalyzer " + analyzer + " failed", ex);
}
}
return null;
}
private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
// 从spring.factory文件中读取报告器
List<FailureAnalysisReporter> reporters = SpringFactoriesLoader
.loadFactories(FailureAnalysisReporter.class, classLoader);
if (analysis == null || reporters.isEmpty()) {
return false;
}
// 遍历调用报告接口,生成报告
for (FailureAnalysisReporter reporter : reporters) {
reporter.report(analysis);
}
return true;
}
分析器
分析接口:FailureAnalyzer 抽象类:AbstractFailureAnalyzer->analyze(Throwable failure)
public FailureAnalysis analyze(Throwable failure) {
// 主要是判断异常实例的类型,然后调用对应的分析器进行分析
T cause = findCause(failure, getCauseType());
if (cause != null) {
return analyze(failure, cause);
}
return null;
}
protected final <E extends Throwable> E findCause(Throwable failure, Class<E> type) {
// 循环判断,是否是指定类型实例
while (failure != null) {
if (type.isInstance(failure)) {
return (E) failure;
}
failure = failure.getCause();
}
return null;
}
具体实现类:PortInUseFailureAnalyzer(其中的一个实现类)
class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
// 对异常信息进行说明,包装成FailureAnalysis对象返回
return new FailureAnalysis(
"Web server failed to start. Port " + cause.getPort()
+ " was already in use.",
"Identify and stop the process that's listening on port "
+ cause.getPort() + " or configure this "
+ "application to listen on another port.",
cause);
}
}
报告器
报告接口:FailureAnalysisReporter 核心方法:void report(FailureAnalysis analysis); // 对分析结果进行报告呈现 具体实现类:LoggingFailureAnalysisReporter,主要进行日志打印
public final class LoggingFailureAnalysisReporter implements FailureAnalysisReporter {
private static final Log logger = LogFactory
.getLog(LoggingFailureAnalysisReporter.class);
@Override
public void report(FailureAnalysis failureAnalysis) {
// debug模式下打印堆栈信息
if (logger.isDebugEnabled()) {
logger.debug("Application failed to start due to an exception",
failureAnalysis.getCause());
}
// error模式下打印自定义错误信息
if (logger.isErrorEnabled()) {
logger.error(buildMessage(failureAnalysis));
}
}
private String buildMessage(FailureAnalysis failureAnalysis) {
StringBuilder builder = new StringBuilder();
builder.append(String.format("%n%n"));
builder.append(String.format("***************************%n"));
builder.append(String.format("APPLICATION FAILED TO START%n"));
builder.append(String.format("***************************%n%n"));
builder.append(String.format("Description:%n%n"));
builder.append(String.format("%s%n", failureAnalysis.getDescription()));
if (StringUtils.hasText(failureAnalysis.getAction())) {
builder.append(String.format("%nAction:%n%n"));
builder.append(String.format("%s%n", failureAnalysis.getAction()));
}
return builder.toString();
}
}
总结
虽然该模块比较简单,但很有指导意义,将异常处理专门封装成一个模块,易于扩展,易于理解 使用了模版方法模式,抽象类AbstractFailureAnalyzer定义了基本流程,子类去实现,扩展性较好 功能模块划分清晰:将整个流程分为分析和报告两个大的阶段,耦合度低