内置 FailureAnalyzer 逐行分析
概述
Spring Boot 内置了 15+ 个 FailureAnalyzer 实现,在应用启动失败时将原始异常转化为人类可读的诊断报告。每个 FailureAnalyzer 针对特定异常类型,提取关键信息并输出问题描述和修复建议。
本文将深入拆解 8 个核心内置 FailureAnalyzer 的实现细节,涵盖缺失 Bean、歧义注入、端口占用、数据源配置、YAML 语法、循环依赖等常见启动失败场景的诊断逻辑。
本文基于 Spring Boot 3.2.5 源码分析。FailureAnalyzer SPI 机制可参考 FailureAnalyzer 与错误分析。
1. NoSuchBeanDefinitionFailureAnalyzer 的 analyze()
NoSuchBeanDefinitionFailureAnalyzer 处理 BeanCreationException 中的 NoSuchBeanDefinitionException,诊断因缺少 Bean 导致的注入失败。
class NoSuchBeanDefinitionFailureAnalyzer
extends AbstractFailureAnalyzer<NoSuchBeanDefinitionException> {
protected FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause) {
// 1. 提取缺失 Bean 的 BeanDefinition
BeanDefinition definition = getBeanDefinition(rootFailure);
// 2. 提取缺失的 Bean 类型
String missingBeanType = cause.getBeanType() != null
? cause.getBeanType().getName()
: cause.getBeanName();
// 3. 构建诊断信息
StringBuilder description = new StringBuilder();
description.append("构造方法 ");
.append(definition.getBeanClassName())
.append(" 的参数 ");
.append(parameterName)
.append(" 需要类型为 ");
.append(missingBeanType)
.append(" 的 Bean,但未找到该 Bean 定义");
// 4. 生成建议
StringBuilder action = new StringBuilder();
action.append("请确保类型为 ")
.append(missingBeanType)
.append(" 的 Bean 已被定义:\n");
action.append(" 1. 使用 @Component / @Service / @Repository 注解\n");
action.append(" 2. 使用 @Bean 在 @Configuration 类中声明\n");
action.append(" 3. 检查 @ComponentScan 是否正确包路径\n");
action.append(" 4. 确保依赖的自动配置已启用(如 spring-boot-starter-*)");
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
}输出示例:
***************************
APPLICATION FAILED TO START
***************************
Description:
---
构造方法 com.example.UserService(UserRepository userRepository) 的参数 0
需要类型为 com.example.UserRepository 的 Bean,但未找到该 Bean 定义
Action:
---
请确保类型为 com.example.UserRepository 的 Bean 已被定义:
1. 使用 @Component / @Service / @Repository 注解
2. 使用 @Bean 在 @Configuration 类中声明
3. 检查 @ComponentScan 是否正确包路径
4. 确保依赖的自动配置已启用(如 spring-boot-starter-*)2. NoUniqueBeanDefinitionFailureAnalyzer 的歧义提示
NoUniqueBeanDefinitionFailureAnalyzer 处理 NoUniqueBeanDefinitionException,当存在多个同类型候选 Bean 时列出所有候选。
class NoUniqueBeanDefinitionFailureAnalyzer
extends AbstractFailureAnalyzer<NoUniqueBeanDefinitionException> {
protected FailureAnalysis analyze(Throwable rootFailure, NoUniqueBeanDefinitionException cause) {
// 1. 获取所有候选 Bean 的名称和类型
Map<String, Object> candidateBeans = getCandidateBeans(cause);
// 2. 提取注入点信息
String injectionPoint = getInjectionPoint(rootFailure);
// 3. 构建诊断信息
StringBuilder description = new StringBuilder();
description.append("在 ").append(injectionPoint).append(" 处注入类型为 ")
.append(cause.getBeanType().getName()).append(" 的 Bean 时,发现多个候选 Bean:\n");
StringBuilder action = new StringBuilder();
action.append("以下候选 Bean 均满足类型匹配:\n");
int i = 1;
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
String beanName = entry.getKey();
Object bean = entry.getValue();
description.append(" Bean ").append(i++).append(": ")
.append(beanName).append(" (").append(bean.getClass().getName()).append(")\n");
action.append(" 方案 ").append(i-1).append(": 使用 @Qualifier(\"").append(beanName).append("\") 指定\n");
}
action.append(" 方案 ").append(i).append(": 使用 @Primary 标注首选 Bean");
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
}输出示例:
Description:
---
在 com.example.OrderService (UserService userService) 处注入类型为
com.example.UserService 的 Bean 时,发现多个候选 Bean:
Bean 1: userServiceA (com.example.UserServiceA)
Bean 2: userServiceB (com.example.UserServiceB)
Action:
---
以下候选 Bean 均满足类型匹配:
方案 1: 使用 @Qualifier("userServiceA") 指定
方案 2: 使用 @Qualifier("userServiceB") 指定
方案 3: 使用 @Primary 标注首选 Bean3. ConnectorStartFailureAnalyzer 的端口占用诊断
ConnectorStartFailureAnalyzer 诊断 Web 服务器端口被占用导致的启动失败。
class ConnectorStartFailureAnalyzer
extends AbstractFailureAnalyzer<ConnectorStartFailedException> {
protected FailureAnalysis analyze(Throwable rootFailure, ConnectorStartFailedException cause) {
// 1. 获取被占用的端口号
int port = cause.getPort();
// 2. 获取协议类型(HTTP / HTTPS / AJP)
String protocol = cause.getProtocol();
// 3. 构建诊断信息
String description = String.format(
"Web 服务器 %s 连接器启动失败,端口 %d 已被占用",
protocol, port);
String action = String.format(
"解决方案:\n" +
" 1. 查找占用进程:netstat -ano | findstr :%d\n" +
" 2. 终止占用进程:taskkill /PID <PID> /F\n" +
" 3. 修改端口配置:server.port=%d\n" +
" 4. 使用随机端口:server.port=0",
port, port + 1);
return new FailureAnalysis(description, action, cause);
}
}输出示例:
Description:
---
Web 服务器 HTTP 连接器启动失败,端口 8080 已被占用
Action:
---
解决方案:
1. 查找占用进程:netstat -ano | findstr :8080
2. 终止占用进程:taskkill /PID <PID> /F
3. 修改端口配置:server.port=8081
4. 使用随机端口:server.port=0触发场景:
// TomcatServletWebServerFactory 启动时
// ProtocolHandler.init() → ServerSocket.bind(port)
// 如果端口已被占用 → java.net.BindException
// → Tomcat 抛出 ConnectorStartFailedException(port)
// → ConnectorStartFailureAnalyzer 捕获并诊断4. DataSourceBeanCreationFailureAnalyzer 的 3 类诊断
DataSourceBeanCreationFailureAnalyzer 诊断数据源创建失败,涵盖网络不通、密码错误、驱动类不存在 3 类场景。
class DataSourceBeanCreationFailureAnalyzer
extends AbstractFailureAnalyzer<DataSourceCreationException> {
protected FailureAnalysis analyze(Throwable rootFailure, DataSourceCreationException cause) {
// 1. 提取数据源配置 URL
String url = extractUrl(rootFailure);
// 2. 提取用户名
String username = extractUsername(rootFailure);
// 3. 提取驱动类名
String driverClassName = extractDriverClassName(rootFailure);
// 4. 遍历异常链,细分诊断
Throwable chain = rootFailure;
while (chain != null) {
if (chain instanceof java.net.ConnectException) {
// 场景 1:网络不通
return new FailureAnalysis(
"数据源连接失败:无法连接到数据库服务器\n" +
" URL: " + url,
"排查建议:\n" +
" 1. 确认数据库服务是否已启动\n" +
" 2. 检查 URL 中的主机名和端口号是否正确\n" +
" 3. telnet <主机> <端口> 测试连通性\n" +
" 4. 检查防火墙/安全组规则",
chain);
}
if (chain instanceof java.sql.SQLException sqlEx) {
String sqlState = sqlEx.getSQLState();
if ("28000".equals(sqlState)) {
// 场景 2:密码错误/认证失败
return new FailureAnalysis(
"数据源认证失败:用户名或密码错误\n" +
" 用户: " + username + "\n" +
" URL: " + url,
"排查建议:\n" +
" 1. 检查 spring.datasource.username/password 配置\n" +
" 2. 确认密码是否包含特殊字符需转义\n" +
" 3. 使用数据库客户端手动登录验证",
chain);
}
}
if (chain instanceof ClassNotFoundException) {
// 场景 3:驱动类不存在
return new FailureAnalysis(
"找不到数据库驱动类:" + driverClassName,
"排查建议:\n" +
" 1. 确认已添加对应的数据库驱动依赖\n" +
" 2. 检查驱动类名是否正确:\n" +
" - MySQL: com.mysql.cj.jdbc.Driver\n" +
" - PostgreSQL: org.postgresql.Driver\n" +
" - H2: org.h2.Driver",
chain);
}
chain = chain.getCause();
}
// 通用诊断兜底
return new FailureAnalysis(
"数据源创建失败,URL: " + url,
"通用排查:\n 1. 确认 spring.datasource.url 配置正确\n" +
" 2. 确认数据库已启动\n 3. 检查驱动依赖是否完整",
cause);
}
}3 类诊断对比:
| 场景 | 异常标志 | 诊断输出 |
|---|---|---|
| 网络不通 | ConnectException | 无法连接到数据库服务器 + 排查网络连通性 |
| 密码错误 | SQLException(sqlState=28000) | 用户名或密码错误 + 检查配置 |
| 驱动类不存在 | ClassNotFoundException | 找不到数据库驱动类 + 添加依赖 |
5. PortInUseFailureAnalyzer 的端口扫描
PortInUseFailureAnalyzer 专注于诊断端口被占用问题,提供更精细的端口信息。
class PortInUseFailureAnalyzer
extends AbstractFailureAnalyzer<BindException> {
protected FailureAnalysis analyze(Throwable rootFailure, BindException cause) {
// 1. 从 BindException 消息中提取端口号
String message = cause.getMessage();
int port = extractPort(message);
// 2. 查找可用端口(供用户参考)
int availablePort = SocketUtils.findAvailableTcpPort(port + 1, port + 1000);
// 3. 构建诊断信息
String description = message.contains("Address already in use")
? "端口 " + port + " 已被其他进程占用"
: "无法绑定端口 " + port + ":" + message;
StringBuilder action = new StringBuilder();
action.append("排查步骤:\n");
action.append(" 1. 查找占用进程:\n");
action.append(" netstat -ano | findstr :").append(port).append("\n");
action.append(" 2. 或使用 PowerShell:\n");
action.append(" Get-Process -Id (Get-NetTCPConnection -LocalPort ").append(port).append(").OwningProcess\n");
action.append(" 3. 修改端口配置,例如改为 ").append(availablePort).append(":\n");
action.append(" server.port=").append(availablePort).append("\n");
action.append(" 4. 或使用随机端口:server.port=0");
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
// 从异常消息中提取端口号
private int extractPort(String message) {
// BindException 消息格式:
// " Address already in use: bind: 8080
// 或 "Cannot assign requested address: bind: 8080"
Matcher matcher = Pattern.compile(":\\s*(\\d+)").matcher(message);
return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
}
}SocketUtils.findAvailableTcpPort() 的工作方式:
public abstract class SocketUtils {
public static int findAvailableTcpPort(int minPort, int maxPort) {
for (int port = minPort; port <= maxPort; port++) {
try (ServerSocket ss = new ServerSocket(port)) {
// 尝试绑定端口,成功则返回
return port;
} catch (IOException ignored) {
// 端口被占用,尝试下一个
}
}
throw new IllegalStateException("无可用的 TCP 端口(范围: " + minPort + "-" + maxPort + ")");
}
}输出示例:
Description:
---
端口 8080 已被其他进程占用
Action:
---
排查步骤:
1. 查找占用进程:
netstat -ano | findstr :8080
2. 或使用 PowerShell:
Get-Process -Id (Get-NetTCPConnection -LocalPort 8080).OwningProcess
3. 修改端口配置,例如改为 8081:
server.port=8081
4. 或使用随机端口:server.port=06. BindValidationFailureAnalyzer 的配置校验
BindValidationFailureAnalyzer 处理 BindException 导致的配置校验失败,列出所有 FieldError 并提供详细定位。
class BindValidationFailureAnalyzer
extends AbstractFailureAnalyzer<BindException> {
protected FailureAnalysis analyze(Throwable rootFailure, BindException cause) {
// 1. 提取所有字段错误
List<FieldError> fieldErrors = cause.getFieldErrors();
// 2. 获取绑定目标的类名
String targetName = cause.getTarget() != null
? cause.getTarget().getClass().getSimpleName()
: "Unknown";
// 3. 构建诊断信息
StringBuilder description = new StringBuilder();
description.append("配置属性绑定失败(目标类: ").append(targetName).append(")\n");
description.append("共发现 ").append(fieldErrors.size()).append(" 个校验错误:\n\n");
for (int i = 0; i < fieldErrors.size(); i++) {
FieldError fe = fieldErrors.get(i);
description.append(" ").append(i + 1).append(". ")
.append("属性: ").append(fe.getObjectName()).append(".").append(fe.getField()).append("\n");
description.append(" 配置值: ").append(fe.getRejectedValue()).append("\n");
description.append(" 预期类型: ").append(fe.getFieldType().getSimpleName()).append("\n");
description.append(" 校验信息: ").append(fe.getDefaultMessage()).append("\n");
}
// 4. 生成建议
StringBuilder action = new StringBuilder();
action.append("请检查以下配置项:\n");
for (FieldError fe : fieldErrors) {
action.append(" - ").append(fe.getObjectName()).append(".").append(fe.getField())
.append(":当前值为 '").append(fe.getRejectedValue())
.append("',期望为有效的 ").append(fe.getFieldType().getSimpleName())
.append("\n");
}
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
}输出示例:
Description:
---
配置属性绑定失败(目标类: DataSourceProperties)
共发现 2 个校验错误:
1. 属性: datasource.url
配置值: jdbc:mysql://localhost:3306/db
预期类型: URL
校验信息: URL 格式无效
2. 属性: datasource.username
配置值: admin
预期类型: String
校验信息: 长度不能少于 5 个字符
Action:
---
请检查以下配置项:
- datasource.url:当前值为 'jdbc:mysql://localhost:3306/db',期望为有效的 URL
- datasource.username:当前值为 'admin',期望为有效的 String7. YamlParseFailureAnalyzer 的 YAML 语法错误
YamlParseFailureAnalyzer 处理 YamlException,定位 YAML 文件中的语法错误并输出行号和上下文。
class YamlParseFailureAnalyzer
extends AbstractFailureAnalyzer<YamlException> {
protected FailureAnalysis analyze(Throwable rootFailure, YamlException cause) {
// 1. 从异常中提取文件路径和行号
String filePath = extractFilePath(cause);
int lineNumber = extractLineNumber(cause);
String detail = cause.getMessage();
// 2. 提取错误上下文(前后各 2 行)
String context = extractYamlContext(filePath, lineNumber);
// 3. 构建诊断信息
StringBuilder description = new StringBuilder();
description.append("YAML 配置文件解析失败\n");
description.append(" 文件: ").append(filePath).append("\n");
if (lineNumber > 0) {
description.append(" 行号: ").append(lineNumber).append("\n");
}
description.append(" 详情: ").append(detail).append("\n");
if (context != null) {
description.append("\n错误上下文:\n").append(context);
}
// 4. 生成建议
StringBuilder action = new StringBuilder();
action.append("YAML 常见语法错误:\n");
action.append(" 1. 缩进不一致:请使用空格缩进(推荐 2 个空格),不能使用 Tab\n");
action.append(" 2. 键值对格式:key: value,冒号后需要一个空格\n");
action.append(" 3. 列表格式:- item,短横线后需要一个空格\n");
action.append(" 4. 字符串引号:包含特殊字符(: # { } [ ] > |)的字符串请加引号\n");
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
private int extractLineNumber(YamlException cause) {
// SnakeYaml 的异常消息中有时会包含行号
// 格式: "while parsing a block mapping at line 5, column 3"
Matcher matcher = Pattern.compile("line (\\d+)").matcher(cause.getMessage());
return matcher.find() ? Integer.parseInt(matcher.group(1)) : -1;
}
private String extractYamlContext(String filePath, int lineNumber) {
if (lineNumber <= 0 || filePath == null) return null;
try {
List<String> lines = java.nio.file.Files.readAllLines(java.nio.file.Paths.get(filePath));
int start = Math.max(0, lineNumber - 3);
int end = Math.min(lines.size(), lineNumber + 2);
StringBuilder sb = new StringBuilder();
for (int i = start; i < end; i++) {
String prefix = (i == lineNumber - 1) ? "→ " : " ";
sb.append(prefix).append(String.format("%4d", i + 1)).append(": ").append(lines.get(i)).append("\n");
}
return sb.toString();
} catch (IOException ignored) {
return null;
}
}
}输出示例:
Description:
---
YAML 配置文件解析失败
文件: C:\workspace\app\src\main\resources\application.yml
行号: 10
详情: while scanning a simple key at line 10, column 1:
could not find expected ':' at line 11, column 2
错误上下文:
8: spring:
9: datasource:
10: url: jdbc:mysql://localhost:3306/db
11: username: admin
Action:
---
YAML 常见语法错误:
1. 缩进不一致:请使用空格缩进(推荐 2 个空格),不能使用 Tab
2. 键值对格式:key: value,冒号后需要一个空格
3. 列表格式:- item,短横线后需要一个空格
4. 字符串引号:包含特殊字符(: # { } [ ] > |)的字符串请加引号8. BeanCurrentlyInCreationFailureAnalyzer 的循环依赖
BeanCurrentlyInCreationFailureAnalyzer 诊断 BeanCurrentlyInCreationException,输出循环依赖链路径。
class BeanCurrentlyInCreationFailureAnalyzer
extends AbstractFailureAnalyzer<BeanCurrentlyInCreationException> {
protected FailureAnalysis analyze(Throwable rootFailure, BeanCurrentlyInCreationException cause) {
// 1. 构建依赖链
List<String> dependencyChain = buildDependencyChain(rootFailure);
// 2. 获取出问题的 Bean 名称
String beanName = cause.getBeanName();
// 3. 构建诊断信息
StringBuilder description = new StringBuilder();
description.append("循环依赖检测失败:Bean '")
.append(beanName).append("' 正在创建中\n");
description.append("\n依赖链:\n");
for (int i = 0; i < dependencyChain.size(); i++) {
String indent = " " + " ".repeat(i);
description.append(indent).append("↓ ").append(dependencyChain.get(i)).append("\n");
}
// 4. 构建建议
StringBuilder action = new StringBuilder();
action.append("循环依赖的解决方案:\n\n");
action.append(" 1. 使用 @Lazy 延迟加载(推荐临时方案):\n");
action.append(" @Lazy @Autowired\n");
action.append(" private A a;\n\n");
action.append(" 2. 重构代码,提取公共依赖到第三个类:\n");
action.append(" 目前: A ←→ B(相互引用)\n");
action.append(" 重构: A → C ← B(提取 C 为公共依赖)\n\n");
action.append(" 3. 使用构造器注入 + @Lazy(推荐):\n");
action.append(" public A(@Lazy B b) { this.b = b; }\n\n");
action.append(" 4. 使用 Setter 注入替代构造器注入:\n");
action.append(" @Autowired\n");
action.append(" public void setB(B b) { this.b = b; }");
return new FailureAnalysis(description.toString(), action.toString(), cause);
}
private List<String> buildDependencyChain(Throwable rootFailure) {
List<String> chain = new ArrayList<>();
// 从异常栈中提取 Bean 创建顺序
// 分析栈帧中 AbstractAutowireCapableBeanFactory.doCreateBean() 的调用链
for (StackTraceElement element : rootFailure.getStackTrace()) {
if (element.getClassName().contains("AbstractAutowireCapableBeanFactory")
&& element.getMethodName().equals("doCreateBean")) {
// 从栈帧中提取正在创建的 Bean 名称
chain.add(extractBeanNameFromStack(element));
}
}
return chain;
}
}输出示例:
Description:
---
循环依赖检测失败:Bean 'a' 正在创建中
依赖链:
↓ A (单例)
↓ B (单例: 被 A 的 @Autowired 依赖)
↓ A (单例: 被 B 的 @Autowired 依赖,形成循环)
Action:
---
循环依赖的解决方案:
1. 使用 @Lazy 延迟加载(推荐临时方案):
@Lazy @Autowired
private A a;
2. 重构代码,提取公共依赖到第三个类:
目前: A ←→ B(相互引用)
重构: A → C ← B(提取 C 为公共依赖)
3. 使用构造器注入 + @Lazy(推荐):
public A(@Lazy B b) { this.b = b; }
4. 使用 Setter 注入替代构造器注入:
@Autowired
public void setB(B b) { this.b = b; }典型循环依赖场景:
@Component
public class A {
private final B b;
public A(B b) { // A 需要 B
this.b = b;
}
}
@Component
public class B {
private final A a;
public B(A a) { // B 需要 A → 循环
this.a = a;
}
}Bean 创建栈分析:
A.doCreateBean() ← A 开始创建
→ populateBean() ← 填充属性
→ autowired B ← 发现需要注入 B
→ B.doCreateBean() ← B 开始创建
→ populateBean() ← 填充属性
→ autowired A ← 发现需要注入 A → 但 A 正在创建中
→ BeanCurrentlyInCreationException("A")更多内置 FailureAnalyzer 一览
除上述 8 个重点分析的 FailureAnalyzer 外,Spring Boot 还内置了以下 Analyzer:
| FailureAnalyzer | 关联异常 | 诊断场景 |
|---|---|---|
HikariDriverConfigurationFailureAnalyzer | HikariConfigurationException | HikariCP 驱动类与 URL 不匹配 |
RedisFailureAnalyzer | RedisConnectionFailureException | Redis 连接失败 |
MongoDBFailureAnalyzer | MongoException | MongoDB 连接或认证失败 |
KafkaFailureAnalyzer | KafkaException | Kafka 连接失败 |
WebClientFailureAnalyzer | WebClientRequestException | WebClient HTTP 请求失败 |
NoSuchMethodFailureAnalyzer | NoSuchMethodError | JAR 包版本冲突导致方法找不到 |
JpaFailureAnalyzer | JPAException | JPA/Hibernate 实体映射错误 |
AutoConfigurationFailureAnalyzer | AutoConfigurationImportException | 自动配置类内部异常 |
FailureAnalysisReporter 的输出格式
所有 FailureAnalyzer 的分析结果通过 FailureAnalysisReporter 输出到控制台。
public final class LoggingFailureAnalysisReporter implements FailureAnalysisReporter {
@Override
public void report(FailureAnalysis analysis) {
if (logger.isErrorEnabled()) {
StringBuilder builder = new StringBuilder();
builder.append("\n***************************\n");
builder.append("APPLICATION FAILED TO START\n");
builder.append("***************************\n\n");
builder.append("Description:\n---\n");
builder.append(analysis.getDescription()).append("\n\n");
builder.append("Action:\n---\n");
builder.append(analysis.getAction()).append("\n");
logger.error(builder.toString(), analysis.getCause());
}
}
}完整的启动失败输出示例:
***************************
APPLICATION FAILED TO START
***************************
Description:
---
构造方法 com.example.UserService(UserRepository userRepository)
的参数 0 需要类型为 com.example.UserRepository 的 Bean,
但未找到该 Bean 定义
Action:
---
请确保类型为 com.example.UserRepository 的 Bean 已被定义:
1. 使用 @Component / @Service / @Repository 注解
2. 使用 @Bean 在 @Configuration 类中声明
3. 检查 @ComponentScan 是否正确包路径
4. 确保依赖的自动配置已启用(如 spring-boot-starter-*)三种分析输出的结构:
| 角色 | 接口/类 | 职责 |
|---|---|---|
FailureAnalyzer | SPI 接口 | 将异常 → FailureAnalysis(description + action) |
FailureAnalysis | 数据传输对象 | 存储 description、action、cause |
FailureAnalysisReporter | SPI 接口 | 输出 FailureAnalysis 到日志/控制台 |
总结
内置 FailureAnalyzer 的 8 个细节点总结如下:
| # | 细节点 | 核心类/机制 |
|---|---|---|
| ① | NoSuchBeanDefinitionFailureAnalyzer 的 analyze() | 解析 BeanCreationException → 提取缺失 Bean 类型 → 输出建议 |
| ② | NoUniqueBeanDefinitionFailureAnalyzer 的歧义提示 | 列出所有候选 Bean 的名称和位置,建议使用 @Qualifier / @Primary |
| ③ | ConnectorStartFailureAnalyzer 的端口占用诊断 | 解析 ConnectorStartFailedException → 输出被占用的端口号和协议 |
| ④ | DataSourceBeanCreationFailureAnalyzer 的 3 类诊断 | 网络不通 / 密码错误 / 驱动类不存在,遍历异常链细分诊断 |
| ⑤ | PortInUseFailureAnalyzer 的端口扫描 | BindException: Address already in use → SocketUtils.findAvailableTcpPort() 推荐可用端口 |
| ⑥ | BindValidationFailureAnalyzer 的配置校验 | BindException → FieldError 列表 → 输出配置路径和错误值 |
| ⑦ | YamlParseFailureAnalyzer 的 YAML 语法错误 | YamlException → 输出行号和错误上下文 |
| ⑧ | BeanCurrentlyInCreationFailureAnalyzer 的循环依赖 | BeanCurrentlyInCreationException → 输出依赖链路径和解决方案 |