You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot独立应用启动错误自定义:@ControllerAdvice失效求助

Spring Boot启动阶段端口占用异常自定义提示解决方案

问题场景

开发面向新手的Java独立Spring Boot应用,希望自定义启动失败时的错误提示(比如端口7000被占用的场景),尝试使用@ControllerAdvice捕获ApplicationContextException但未生效。

启动日志示例

2025-01-02 15:33:14 - [] - [WARN] - [org.springframework.context.support.AbstractApplicationContext.refresh] - 上下文初始化期间遇到异常 - 取消刷新尝试: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'
2025-01-02 15:33:14 - [] - [INFO] - [org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLogger.logMessage] - 

ApplicationContext启动失败。如需显示条件评估报告,请启用'debug'模式重新运行应用。
2025-01-02 15:33:14 - [] - [ERROR] - [org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report] - 

***************************
应用启动失败
***************************

描述:
Web服务器启动失败。端口7000已被占用。

操作建议:
找到并停止占用端口7000的进程,或配置应用监听其他端口。

尝试的无效代码

@ControllerAdvice
public class GenericExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GenericExceptionHandler.class);

   
    @ExceptionHandler(ApplicationContextException.class)
    public void handleApplicationContextException(ApplicationContextException exception) {
        log.error("Port Already in use, Refer Step 3.1 for configuring new port");
    }
}

原因分析

@ControllerAdvice是用于处理Web请求阶段的异常,它依赖Spring上下文初始化完成后才能被加载并生效。而端口占用导致的启动失败发生在Spring上下文刷新阶段,此时@ControllerAdvice注解的Bean还未被实例化,因此无法捕获该阶段的异常。

有效解决方案

方案1:自定义FailureAnalyzer(Spring Boot官方推荐)

Spring Boot提供FailureAnalyzer接口用于处理启动失败的诊断逻辑,可直接替换默认的错误提示。

  1. 实现自定义FailureAnalyzer
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.web.server.PortInUseException;

public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<PortInUseException> {

    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
        // 自定义错误描述
        String description = String.format("端口 %d 已被占用,应用无法启动", cause.getPort());
        // 自定义新手友好的操作步骤
        String action = String.format("请按以下步骤解决:\n" +
                "1. 查找占用端口的进程:\n" +
                "   - Windows:打开命令提示符,执行 `netstat -ano | findstr :%d`\n" +
                "   - Mac/Linux:打开终端,执行 `lsof -i :%d`\n" +
                "2. 根据输出的进程ID终止进程:\n" +
                "   - Windows:执行 `taskkill /PID 进程ID /F`\n" +
                "   - Mac/Linux:执行 `kill -9 进程ID`\n" +
                "3. 或者修改应用配置文件application.properties,添加 `server.port=新端口号` 更换监听端口",
                cause.getPort(), cause.getPort());
        return new FailureAnalysis(description, action, cause);
    }
}
  1. 注册FailureAnalyzer
    在项目的src/main/resources/META-INF/目录下创建spring.factories文件,添加以下内容:
org.springframework.boot.diagnostics.FailureAnalyzer=com.yourpackage.PortInUseFailureAnalyzer

将com.yourpackage替换为你实际的包路径。

方案2:监听ApplicationFailedEvent事件

通过Spring事件机制监听启动失败事件,在事件回调中自定义错误日志。

  1. 实现事件监听器
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.web.server.PortInUseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StartupFailureListener implements ApplicationListener<ApplicationFailedEvent> {

    private static final Logger log = LoggerFactory.getLogger(StartupFailureListener.class);

    @Override
    public void onApplicationEvent(ApplicationFailedEvent event) {
        Throwable cause = event.getException();
        // 逐层查找根源异常PortInUseException
        while (cause != null) {
            if (cause instanceof PortInUseException) {
                PortInUseException portException = (PortInUseException) cause;
                log.error("\n==================== 启动失败提示 ====================");
                log.error("❌ 端口 {} 已被占用!", portException.getPort());
                log.error("\n📝 解决步骤:");
                log.error("1. 查找占用端口的进程:");
                log.error("   Windows:`netstat -ano | findstr :{}`", portException.getPort());
                log.error("   Mac/Linux:`lsof -i :{}`", portException.getPort());
                log.error("2. 终止进程:");
                log.error("   Windows:`taskkill /PID 进程ID /F`");
                log.error("   Mac/Linux:`kill -9 进程ID`");
                log.error("3. 或修改配置文件application.properties,设置 `server.port=新端口号`");
                log.error("=====================================================");
                break;
            }
            cause = cause.getCause();
        }
    }
}
  1. 注册监听器
    通过配置类将监听器注册到Spring容器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class StartupConfig {

    @Bean
    public StartupFailureListener startupFailureListener() {
        return new StartupFailureListener();
    }
}

效果说明

使用上述任意一种方案,当端口被占用时,应用会输出你自定义的、更适合新手理解的错误提示,帮助用户快速排查问题。

内容的提问来源于stack exchange,提问作者basil daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:33:17