springboot源码-诊断分析器模块

本文深入剖析SpringBoot诊断分析模块的工作流程与源码细节,包括分析器与报告器的实现,以及如何处理启动失败的异常,提供清晰的错误报告。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

诊断分析模块简介

  1. springboot在启动过程中,会对启动失败的原因及异常进行诊断,并打印报告

工作流程

  1. 初始化阶段:加载springboot内置配置的分析器
  2. 准备阶段:设置bean工厂/环境配置
  3. 分析阶段:遍历调用分析器,只要有分析结果返回
  4. 报告阶段:将分析结果进行整理格式化,方便用户解读

源码分析

  1. 诊断分析模块主要分为:分析器、报告呈现
分析入口类
  1. 入口类:FailureAnalyzers
  2. 构造器
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;
}
  1. 分析报告核心方法
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;
}
分析器
  1. 分析接口:FailureAnalyzer
  2. 抽象类: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;
}
  1. 具体实现类: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);
	}
}
报告器
  1. 报告接口:FailureAnalysisReporter
  2. 核心方法:void report(FailureAnalysis analysis); // 对分析结果进行报告呈现
  3. 具体实现类: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();
	}

}

总结

  1. 虽然该模块比较简单,但很有指导意义,将异常处理专门封装成一个模块,易于扩展,易于理解
  2. 使用了模版方法模式,抽象类AbstractFailureAnalyzer定义了基本流程,子类去实现,扩展性较好
  3. 功能模块划分清晰:将整个流程分为分析和报告两个大的阶段,耦合度低
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值