Spring Boot 启动失败?解密 Address already in use 的“幕后真凶”

下面是遇到的 Address already in use 是每一位后端开发者都必然会遇到的经典问题。将这次的调试经历写成一篇博客,不仅能加深您自己的理解,也能帮助无数后来者快速解决问题。

下面是为您精心撰写的技术博客。


Spring Boot 启动失败?解密 Address already in use 的“幕后真凶”

“代码没动,怎么突然就启动不了了?”

这或许是每个 Spring Boot 开发者都曾发出的灵魂拷问。你满怀信心地点击 IDE (Integrated Development Environment, 集成开发环境) 中的运行按钮,期望看到熟悉的启动日志,结果却被一盆冷水浇下——控制台打印出一长串红色的错误堆栈,最后以一段令人沮丧的 APPLICATION FAILED TO START 告终。

如果你仔细观察日志,很可能会发现一个“老朋友”:java.net.BindException: Address already in use

本文将带你深入分析这个 Spring Boot 启动时最常见的“拦路虎”,通过解读真实的启动日志,教你如何像侦探一样,快速定位并“干掉”那个占用你端口的“幕后真凶”。

案发现场:一份典型的失败日志

一切都从一次看似正常的应用启动开始。然而,日志在打印到 Starting ProtocolHandler ["http-nio-8087"] 之后,风云突变:

2025-09-01 18:57:31.254 ERROR 4445 --- [main] o.a.c.util.LifecycleBase   : Failed to start component [Connector[HTTP/1.1-8087]]

org.apache.catalina.LifecycleException: Protocol handler start failed
	...
Caused by: java.net.BindException: Address already in use
	...

最后,Spring Boot 给出了非常友好的“尸检报告”:

***************************
APPLICATION FAILED TO START
***************************

Description:

The Tomcat connector configured to listen on port 8087 failed to start. The port may already be in use or the connector may be misconfigured.

Action:

Verify the connector's configuration, identify and stop any process that's listening on port 8087, or configure this application to listen on another port.

探寻根源:端口被占用的本质

这份日志虽然长,但核心信息却异常清晰:

  1. Failed to start component [Connector[HTTP/1.1-8087]]: Spring Boot 内嵌的 Tomcat Web 服务器,在尝试启动并监听 8087 端口时失败了。
  2. Caused by: java.net.BindException: Address already in use: 这是问题的根本原因。“地址已被占用”是一个底层的网络异常,意味着操作系统告诉你的 Java 程序:“你想租用 8087 这个‘门牌号’来提供服务,但对不起,这个‘门牌号’已经被别人先租走了。”

那么,这个“别人”是谁呢?

在开发环境中,99% 的情况下,这个“别人”就是上一次未能被正常关闭的、你自己启动的同一个应用程序!

这通常发生在:

  • 你在 IDE 中点击了“停止”按钮,但由于某些原因(比如后台线程、数据库连接池未能正常关闭),Java 进程没有完全退出。
  • 你直接关闭了 IDE,但后台的 Java 进程变成了“孤儿进程”,仍在默默地占用着端口。
  • 你同时打开了多个项目,其中一个已经占用了这个端口。

解决方案:找到并终止“霸占者”

既然知道了问题所在,我们的任务就很明确了:找到那个占用 8087 端口的进程,并强制它“退租”。

在 macOS 或 Linux 系统上 🐧
  1. 打开终端 (Terminal)

  2. 第一步:找出“租客”的身份证号 (PID)
    使用 lsof (list open files) 命令,查找谁在使用 8087 端口:

    lsof -i :8087
    

    你会看到类似这样的输出:

    COMMAND   PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
    java    44455   dgq   50u  IPv6 0x1234567890abcdef      0t0  TCP *:8087 (LISTEN)
    

    这里的 PID (Process ID, 进程ID) 44455 就是我们要找的目标。

  3. 第二步:请“租客”离开
    使用 kill 命令,根据 PID 强制终止这个进程:

    kill -9 44455
    

    -9 参数代表一个“无法拒绝”的强制终止信号,能确保进程被彻底清理。

在 Windows 系统上 🪟
  1. 打开命令提示符 (CMD) 或 PowerShell (建议以管理员身份运行)。

  2. 第一步:找出“租客”的身份证号 (PID)
    使用 netstat 命令:

    netstat -ano | findstr :8087
    

    你会看到类似这样的输出:

    TCP    0.0.0.0:8087           0.0.0.0:0              LISTENING       12345
    

    最后一列的数字 12345 就是 PID。

  3. 第二步:请“租客”离开
    使用 taskkill 命令:

    taskkill /PID 12345 /F
    

    /F 参数表示强制终止。

预防胜于治疗:如何避免问题?

  • 使用 IDE 的重启功能: 相比于“停止”再“启动”,直接使用 IDE 的“重启 (Restart)”或“Rerun”功能,通常能更好地管理进程的生命周期。
  • 优雅停机 (Graceful Shutdown): 确保你的应用配置了 Spring Boot Actuator 的 /shutdown 端点,并在 application.properties 中启用它,这能让应用更可靠地关闭。
  • 修改端口: 如果你确实需要同时运行多个服务,最简单的办法就是为它们配置不同的端口号,例如在 application.properties 中设置 server.port=8088

结语

Address already in use 是每个后端开发者成长路上的“必修课”。虽然初见时可能会让人手足无措,但一旦你理解了其背后的“端口独占”原理,并掌握了 lsof/netstatkill/taskkill 这套“侦查-行动”组合拳,这个问题就会从一个恼人的“拦路虎”,变成一个你只需30秒就能轻松解决的“小插曲”。

下次再遇到它,请不要慌张。深呼吸,打开终端,开始你的“端口猎杀”之旅吧!


✨ 总结与图表回顾 📊

📝 问题与解决方案总结表
遇到的问题 ❓根本原因 🧐关键命令 (macOS/Linux) 🐧关键命令 (Windows) 🪟
APPLICATION FAILED TO START端口被占用lsof -i :<端口号>netstat -ano | findstr :<端口号>
BindException: Address already in use旧的应用进程未完全退出kill -9 <PID>taskkill /PID <PID> /F
🗺️ 调试流程图 (Flowchart)
否 (Windows)
🐞 Spring Boot 启动失败
查看日志, 发现 'Address already in use'
操作系统是 macOS/Linux?
执行 lsof -i :<端口号> 查找 PID
执行 netstat -ano 查找 PID
执行 kill -9
执行 taskkill /PID /F
重新启动 Spring Boot 应用
🎉 应用成功启动
🔄 交互时序图 (Sequence Diagram)
开发者"旧的应用进程 (PID: 44455)""新的应用进程""操作系统"1. 启动时绑定并监听 8087 端口启动新应用时发生冲突2. 尝试启动3. 请求绑定 8087 端口4. 💥 拒绝!端口已被占用 (BindException)手动解决5. lsof / netstat 查找 PID6. 返回 PID: 444557. kill / taskkill 终止 PID 444558. 强制关闭进程9. 再次尝试启动10. 请求绑定 8087 端口11. ✅ 成功!开发者"旧的应用进程 (PID: 44455)""新的应用进程""操作系统"
🚦 端口状态图 (State Diagram)
"初始状态"
"旧进程启动并绑定"
"新进程尝试绑定 (失败)"
"执行 kill / taskkill"
Available
In-Use
端口可用
端口被占用
🏗️ 系统组件类图 (Class Diagram)
"请求"
"管理"
1
*
OperatingSystem
-Map portProcessMap
+bindPort(int port, Process process)
+findProcessByPort(int port) : Process
+killProcess(int pid)
Process
+int pid
+start()
+stop()
SpringBootApplication
-int serverPort
+run()
🔗 实体关系图 (Entity Relationship Diagram)
PROCESSintPIDPK进程IDstringname进程名 (e.g., java)PORTintnumberPK端口号 (e.g., 8087)stringstatus状态 (LISTENING)监听
🧠 思维导图 (Markdown Format)

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值