SpringBoot之异常报告器

时光总嘲笑我的痴心妄想 提交于 2020-03-18 16:52:54

一、异常报告器介绍

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

打印输出错误信息。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!