文档站点
Skip to content

为什么 Java 没有自带 HTTP 服务器? ​

Go 写一个 http.ListenAndServe 就能起 Web 服务器,go run main.go 直接跑;PHP 配个 Nginx + PHP-FPM。Java 呢?语言本身没有 HTTP,Web 应用要跑在「Servlet 容器」里,最有名的就是 Tomcat。这对从 Go/PHP 转来的人非常反直觉,这篇讲清楚来龙去脉。

三种语言的 Web 服务器形态 ​

语言服务器从哪来部署形态
Go标准库自带 net/http编译一个二进制直接跑
PHP外部 PHP-FPM + Nginx请求先进 Nginx,再转 PHP
Java(传统)Servlet 容器(Tomcat / Jetty / Undertow)应用打成 war 放进容器跑
Java(Spring Boot)内置容器(默认 Tomcat)java -jar 直接跑,容器打进 jar

为什么 Java 语言不内置 HTTP ​

1. Java 是「通用语言」,不是「服务端专用」 ​

  • Go 从诞生就是「服务端语言」,net/http 是核心标准库,开发者开箱即用
  • Java 1995 年诞生时面向的是桌面和企业应用(早期还有 Applet、Swing),HTTP 从来不是语言的职责
  • 标准库 JDK 只提供最底层的 java.net(TCP/UDP socket),连 HTTP 客户端都是后来补的(Java 11 才有 HttpClient)

2. Java 的 Web 走的是「规范 + 容器」路线 ​

Java 的 Web 生态不是「语言内置」,而是:

Servlet 规范(一份接口标准) ← 定义 Web 应用长什么样
      ↓ 由谁实现
Servlet 容器(Tomcat / Jetty / Undertow)← 实现规范、托管应用

Tomcat 就是「Servlet 规范的实现」。你的应用打成一个 war 包,扔进 Tomcat 的 webapps/ 目录,Tomcat 负责:接收 HTTP 请求 → 解析 → 调度你的代码 → 返回响应。同时它管理线程、会话、生命周期。

这不是缺陷,是 Java 早期(1999 年,Java EE)形成的服务端软件架构:容器统一管理,应用只管业务。到今天这套设计依然成立,只是被 Spring Boot 藏起来了。

3. 其实 JDK 有个「山寨版」HTTP 服务器 ​

com.sun.net.httpserver.HttpServer(JDK 自带)能起一个 HTTP 服务,但功能极其简陋:没有 Servlet 规范、路由、过滤器、会话管理,生产没人用。就像 PHP 的 php -S localhost:8000 内置服务器 —— 只够本地调试。

为什么这让其他语言开发者很困扰 ​

困扰 1:我的代码到底跑在哪? ​

  • Go:main() 里自己 ListenAndServe,一切尽在掌握
  • Java:请求进 Tomcat → Servlet 容器 → Spring MVC → 你的 Controller,中间隔了好几层

困扰 2:为什么要「装」一个 Tomcat? ​

因为 Servlet 容器负责的脏活累活(线程管理、请求解析、会话、部署)你不想自己写,所以需要一个容器托管你的应用。PHP 那边是 Nginx + PHP-FPM 干这事,Tomcat 就是 Java 版的「那个东西」,只是它既是 Web 服务器又是应用服务器。

困扰 3:Tomcat / Jetty / Undertow 是什么关系? ​

它们都是 Servlet 容器的不同实现,功能等价、可以互换。Spring Boot 让你换一行依赖就切换:

xml
<!-- 默认 Tomcat,想换 Jetty 就排除 Tomcat 引入 Jetty -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Spring Boot 是怎么「藏起」Tomcat 的 ​

Spring Boot 的杀手锏是嵌入式容器:把 Tomcat 当普通依赖打进可执行 jar,main() 启动时自动起一个 Tomcat 实例。

bash
java -jar app.jar        # 内嵌 Tomcat 随之启动,监听 8080

对比传统 Java Web:

传统 Java WebSpring Boot
装 Tomcat → 打 war → 扔进 webapps嵌入式 Tomcat 打进 jar,java -jar 就跑
一个 Tomcat 托管多个应用每个应用自带容器,独立运行
部署依赖环境里有 Tomcat环境只需 JDK

所以用 Spring Boot 时,你几乎感觉不到 Tomcat 的存在 —— 它的角色和 Go 的 net/http、PHP 的 PHP-FPM 殊途同归:让你的代码能响应 HTTP 请求。

一句话理解 Tomcat

把 Tomcat 想成「托管 Java Web 应用的那个运行时」:类似 Go 标准库 net/http 干的事,也类似 PHP 的 PHP-FPM + Nginx。只是它不是语言自带的,而是一个独立软件 —— 现在被 Spring Boot 内嵌进 jar 里了。

和 Go 的直观对照 ​

需求GoJava(Spring Boot)
起服务器http.ListenAndServe()引入 starter-web,main() 自动起
处理请求http.HandleFunc("/", handler)@GetMapping("/")
运行go run main.go./mvnw spring-boot:run / java -jar
换服务器实现用第三方库换 starter-tomcat → starter-jetty

小结 ​

记住四句话
Java 语言层面没有 HTTP —— 它是通用语言,Web 靠「Servlet 规范 + 容器」实现
Tomcat 是 Servlet 规范的实现,负责托管 Web 应用,类似 PHP-FPM + Nginx 的角色
JDK 自带的 HttpServer 极其简陋,生产没人用
Spring Boot 把 Tomcat 内嵌进 jar,java -jar 和 go run 一样简单,你日常感知不到容器存在

这篇文章想聊的是「为什么 Java 这样设计」;它和 请求生命周期(请求在容器/过滤器/拦截器间的流转)、构建与部署(如何打包成可执行 jar)配合阅读更完整。

面向 PHP / Laravel 开发者的 Spring Boot 中文文档