一句話先說結論: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
at org.apache.catalina.core.StandardServer.start(StandardServer.java:558)
at org.apache.catalina.startup.Catalina.start(Catalina.java:387)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:345)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:431)
Caused by: java.net.BindException: Address already in use: bind
at java.net.PlainSocketImpl.socketBind(Native Method)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:388)
at java.net.ServerSocket.bind(ServerSocket.java:372)
at java.net.ServerSocket.<init>(ServerSocket.java:234)
at org.apache.tomcat.util.net.ServerSocketFactoryImpl.createServerSocket(ServerSocketFactoryImpl.java:83)
at org.apache.tomcat.util.net.ServerSocketFactoryImpl.createServerSocket(ServerSocketFactoryImpl.java:64)
at org.apache.coyote.AbstractProtocol$ProtocolBinding.init(AbstractProtocol.java:535)
... 12 more
同一時刻一個端口只能有一個進程監聽,第二個人再來綁就會失敗。
根因分析
消息本身已經說得很明白: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看看有沒有容器佔著。
References(參考來源)
- Fix: Spring Boot Web server failed to start. Port 8080 was already in use — FixDevsblog
- How to Fix "Port 8080 Already in Use" in Spring Boot — marmo.devblog
- How to Kill a Process Running on a Port — Dan Vegablog
- How to Find Which Process Is Using a Port — techiesdiaryblog
- Spring Boot Port Configuration — runebookblog
