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();
}

打印輸出錯誤信息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM