一句话先说结论:Spring Boot 内嵌的 Web 服务器默认监听 8080,一旦已有进程占着这个端口,JVM 抛
BindException,应用直接启动失败。解决无非两条:找到并结束占用进程,或者换个端口。这是最基础的坑,但每次都有人栽在“IDE 没停干净”上。
背景
Spring Boot 用内嵌容器(默认 Tomcat)让应用“打成 jar 直接跑”,不需要额外部署到独立 Tomcat。带来的一个副作用是:内嵌容器默认绑定 8080 端口。这个端口同时也是 Tomcat 独立版、Jenkins、Nexus、以及各种开发工具默认爱用的端口,冲突概率极高。
现象
启动时立即失败,控制台报:
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
底层异常是 Tomcat 的 BindException:
org.apache.catalina.LifecycleException: Protocol handler start failed
Caused by: java.net.BindException: Address already in use: bind
同一时刻一个端口只能有一个进程监听,第二个人再来绑就会失败。
根因分析
消息本身已经说得很明白:8080 被占了。值得记的是它为什么被占——常见四种:
- 上一个实例没停干净:在 IDE 里又重新点了一次 Run,前一个
java进程还活着,或者java -jar在后台跑着。 - IDE 留下了僵尸进程:IntelliJ/Eclipse 在崩溃或强停后,有时不会真正结束 Java 进程。
- 别的服务默认也用 8080:独立 Tomcat、Jenkins、Nexus、WildFly 等。
- Docker 容器映射了 8080:容器里跑的可能完全是别的服务,但宿主机这一侧的端口绑定照样挡住你的 Spring Boot 应用。
解决方案
方案 A:找到并结束占用进程
一条命令先查出是谁占了端口。
macOS / Linux:
lsof -i :8080
输出里 PID 那列就是进程号,例如:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 18902 marco 48u IPv6 ... 0t0 TCP *:8080 (LISTEN)
先尝试优雅退出,不行再强杀(-9 是最后手段,不留给进程做清理的机会):
kill 18902
# 若还活着
kill -9 18902
一条命令直接查 PID 并杀掉:
kill -9 $(lsof -ti :8080)
Windows:
netstat -ano | findstr :8080
:: 最后那列就是 PID
taskkill /PID 1234 /F
如果是 Docker 占的端口,先定位容器:
docker ps --filter publish=8080
方案 B:换个端口
不想动别人的进程,就让自己的应用换端口。
application.properties:
server.port=8081
application.yml:
server:
port: 8081
命令行临时指定:
java -jar your-app.jar --server.port=8081
环境变量(借助 Spring Boot 的 relaxed binding,SERVER_PORT 会自动映射到 server.port):
export SERVER_PORT=8081
java -jar your-app.jar
方案 C:测试时用随机端口
集成测试或本地起多个实例时,把端口设成 0,让容器选一个空闲端口:
server.port=0
运行时通过 @Value("${local.server.port}") 拿到实际端口。
小结
- 端口占用是“资源冲突”,不是代码问题,先查
lsof/netstat再决定杀还是换。 kill -9是最后手段,能优雅退出就别强杀。- 记住配置优先级:命令行参数 > 环境变量 > 配置文件 > 默认 8080,端口改了没生效多半是更高优先级的来源覆盖了它。
- 预防:IDE 里用 Stop 停干净再 Run;不同服务约定不同端口;顺手
docker ps看看有没有容器占着。