Spring Boot FailureAnalyzer自定义
FailureAnalyzer 用于在启动失败时提供友好的错误分析。
内置FailureAnalyzer
Spring Boot 内置多个分析器:
| 分析器 | 处理场景 |
|---|---|
| PortInUseFailureAnalyzer | 端口被占用 |
| DataSourceFailureAnalyzer | 数据源配置错误 |
| NoSuchBeanDefinitionFailureAnalyzer | Bean不存在 |
| ValidationExceptionFailureAnalyzer | 参数校验失败 |
| BindFailureAnalyzer | 配置绑定失败 |
自定义FailureAnalyzer
定义异常
Java
public class LicenseExpiredException extends RuntimeException {
private final String licenseKey;
private final Date expiredDate;
public LicenseExpiredException(String licenseKey, Date expiredDate) {
super("License expired: " + licenseKey);
this.licenseKey = licenseKey;
this.expiredDate = expiredDate;
}
public String getLicenseKey() { return licenseKey; }
public Date getExpiredDate() { return expiredDate; }
}
实现分析器
Java
public class LicenseExpiredFailureAnalyzer
extends AbstractFailureAnalyzer<LicenseExpiredException> {
@Override
protected FailureAnalysis analyze(
Throwable rootFailure,
LicenseExpiredException cause) {
return new FailureAnalysis(
// 问题描述
String.format("License已过期,Key: %s,过期时间: %s",
cause.getLicenseKey(),
new SimpleDateFormat("yyyy-MM-dd").format(cause.getExpiredDate())),
// 解决建议
"请联系管理员续期或更新License配置",
// 原始异常
cause
);
}
}
注册分析器
properties
# META-INF/spring.factories
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.LicenseExpiredFailureAnalyzer
启动输出示例
Java
***************************
APPLICATION FAILED TO START
***************************
Description:
License已过期,Key: ABC123,过期时间: 2026-01-01
Action:
请联系管理员续期或更新License配置
配置绑定失败分析
Java
public class CustomBindFailureAnalyzer
extends AbstractFailureAnalyzer<BindException> {
@Override
protected FailureAnalysis analyze(
Throwable rootFailure,
BindException cause) {
StringBuilder description = new StringBuilder();
description.append("配置绑定失败:\n");
for (FieldError error : cause.getFieldErrors()) {
description.append(String.format(" - %s: %s (值: %s)\n",
error.getField(),
error.getDefaultMessage(),
error.getRejectedValue()));
}
return new FailureAnalysis(
description.toString(),
"请检查application.yml中的配置项格式是否正确",
cause
);
}
}
Bean缺失分析
Java
public class MissingBeanFailureAnalyzer
extends AbstractFailureAnalyzer<NoSuchBeanDefinitionException> {
@Override
protected FailureAnalysis analyze(
Throwable rootFailure,
NoSuchBeanDefinitionException cause) {
String beanName = cause.getBeanName();
Class<?> beanType = cause.getBeanType();
String description;
if (beanName != null) {
description = String.format(
"未找到名为 '%s' 的Bean,请检查是否已定义或组件扫描路径是否正确",
beanName);
} else {
description = String.format(
"未找到类型为 '%s' 的Bean,请检查是否添加了相关依赖或配置",
beanType.getName());
}
String action = String.format(
"建议操作:\n" +
" 1. 检查是否添加了相关starter依赖\n" +
" 2. 检查@ComponentScan扫描路径\n" +
" 3. 检查是否需要@Enable*注解开启功能");
return new FailureAnalysis(description, action, cause);
}
}
多异常分析
Java
public class CompositeFailureAnalyzer implements FailureAnalyzer {
private final List<FailureAnalyzer> analyzers = Arrays.asList(
new LicenseExpiredFailureAnalyzer(),
new CustomBindFailureAnalyzer()
);
@Override
public FailureAnalysis analyze(Throwable failure) {
for (FailureAnalyzer analyzer : analyzers) {
FailureAnalysis analysis = analyzer.analyze(failure);
if (analysis != null) {
return analysis;
}
}
return null;
}
}
测试FailureAnalyzer
text
class LicenseExpiredFailureAnalyzerTest {
private final LicenseExpiredFailureAnalyzer analyzer =
new LicenseExpiredFailureAnalyzer();
@Test
void testAnalyze() {
LicenseExpiredException ex = new LicenseExpiredException(
"TEST-KEY",
new Date(System.currentTimeMillis() - 86400000L)
);
FailureAnalysis analysis = analyzer.analyze(null, ex);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains("TEST-KEY");
assertThat(analysis.getAction()).contains("续期");
}
}
FailureAnalyzer只处理启动阶段异常,运行时异常需要使用全局异常处理。
要点总结
- 继承AbstractFailureAnalyzer实现特定异常分析
- 返回FailureAnalysis包含描述和建议
- 通过spring.factories注册
- 提升启动失败错误信息的可读性
📝 发现内容有误?点击此处直接编辑