SpringBoot之异常报告器

一、异常报告器介绍

1.1 作用

收集错误信息,用于向用户报告错误原因。

1.2 接口定义

@FunctionalInterface
public interface SpringBootExceptionReporter {
	// 向用户报告失败信息
	boolean reportException(Throwable failure);

}

二、源码解析

2.1 run 初始化

public ConfigurableApplicationContext run(String... args) {
	......
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	
	try {
		......
        // 获取所有 SpringBootExceptionReporter 实现类
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		......
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		......
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
        // 获取 spring.factoryies 中类型为 SpringBootExceptionReporter 的配置。
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 实例化创建对象
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

Spring.facories 中对 SpringBootExceptionReporter 的配置如下:

org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

2.2 handleRunFailure

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
		Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
	try {
		try {
            // 处理异常退出代码
			handleExitCode(context, exception);
            // 如果监听器操作就调用方法
			if (listeners != null) {
				listeners.failed(context, exception);
			}
		}
		finally {
            // 报告失败信息
			reportFailure(exceptionReporters, exception);
			if (context != null) {
				context.close();
			}
		}
	}
	catch (Exception ex) {
		logger.warn("Unable to close ApplicationContext", ex);
	}
	ReflectionUtils.rethrowRuntimeException(exception);
}

2.2.1 查看 handleExitCode 方法:

private void handleExitCode(ConfigurableApplicationContext context, Throwable exception) {
    // 从异常中获取退出代码
	int exitCode = getExitCodeFromException(context, exception);
	if (exitCode != 0) {
		if (context != null) {
			context.publishEvent(new ExitCodeEvent(context, exitCode));
		}
		SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
		if (handler != null) {
			handler.registerExitCode(exitCode);
		}
	}
}

2.2.2 查看 reportFailure

private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
	try {
		for (SpringBootExceptionReporter reporter : exceptionReporters) {
			if (reporter.reportException(failure)) {             
				registerLoggedException(failure);
				return;
			}
		}
	}
	catch (Throwable ex) {
		// Continue with normal handling of the original failure
	}
	if (logger.isErrorEnabled()) {
		logger.error("Application run failed", failure);
		registerLoggedException(failure);
	}
}

protected void registerLoggedException(Throwable exception) {
	SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
	if (handler != null) {
        // 注册异常信息
		handler.registerLoggedException(exception);
	}
}

2.2.3 查看 reporter.reportException

@Override
public boolean reportException(Throwable failure) {
	FailureAnalysis analysis = analyze(failure, this.analyzers);
	return report(analysis, this.classLoader);
}

private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
	List<FailureAnalysisReporter> reporters = SpringFactoriesLoader.loadFactories(FailureAnalysisReporter.class,
			classLoader);
	if (analysis == null || reporters.isEmpty()) {
		return false;
	}
	for (FailureAnalysisReporter reporter : reporters) {
		reporter.report(analysis);
	}
	return true;
}

2.4.4 FailureAnalyzers

最终进入 FailureAnalyzers.reportException

@Override
public boolean reportException(Throwable failure) {
	FailureAnalysis analysis = analyze(failure, this.analyzers);
	return report(analysis, this.classLoader);
}

private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
	List<FailureAnalysisReporter> reporters = SpringFactoriesLoader.loadFactories(FailureAnalysisReporter.class,
			classLoader);
	if (analysis == null || reporters.isEmpty()) {
		return false;
	}
	for (FailureAnalysisReporter reporter : reporters) {
		reporter.report(analysis);
	}
	return true;
}

进入 LoggingFailureAnalysisReporter.report 方法:

@Override
public void report(FailureAnalysis failureAnalysis) {
	if (logger.isDebugEnabled()) {
		logger.debug("Application failed to start due to an exception", failureAnalysis.getCause());
	}
	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();
}

打印输出错误信息。

posted @ 2020-03-18 15:31  MarkLogZhu  阅读(1184)  评论(0编辑  收藏  举报