这是一个在Java世界中至关重要,但对初学者(尤其是从PHP背景过来的开发者)来说常常感到困惑的话题。打包成JAR还是WAR,这个选择直接决定了你的应用程序的架构、部署方式和运维模式。
让我们深入思考这个问题,详细讲解两者的区别、适用场景,并用你熟悉的PHP世界进行类比。
核心思想:从"一堆代码文件"到"一个标准化的包裹"
首先,回顾一下我们之前的讨论。PHP的部署本质上是同步源代码 。你将一个包含index.php
、app/
、vendor/
等目录的文件夹部署到服务器上,由一个已经配置好的Web服务器(Nginx/Apache + PHP-FPM)来解释执行这些代码。部署的单元是一个文件夹。
Java则不同。Java是编译型语言,我们不部署源代码。我们部署的是一个经过编译、测试、打包后生成的标准化的"包裹",也称为"产物"(Artifact) 。这个包裹就是JAR
或WAR
文件。部署的单元是一个文件。
JAR
和WAR
的本质都是ZIP压缩文件,你可以用解压工具打开它们。它们的区别在于内部结构和设计目的,这导致了它们运行方式的根本不同。
JAR包 (Java ARchive) - 内置一切的"集装箱式"应用
想象一下一个自带发电机、空调、所有设备的集装箱,你把它吊到任何一块平地上,接上电,它就能立刻成为一个功能齐全的办公室。这就是现代Spring Boot项目中的可执行JAR (Executable JAR)。
1. 它是什么?
一个可执行JAR包是一个自包含、独立运行 的应用程序。它的内部不仅仅有你写的业务代码(编译后的.class
文件),还内嵌了一个Web服务器(如Tomcat, Jetty或Undertow)。
2. 内部结构 (简化版Spring Boot JAR)
my-app.jar
├── BOOT-INF/
│ ├── classes/ <-- 你编写的业务代码 (.class文件) 和 资源文件 (application.yml)
│ └── lib/ <-- 所有第三方依赖库 (spring-web, hibernate, mysql-driver, etc.jar)
├── META-INF/
│ └── MANIFEST.MF <-- 清单文件,指定了启动类,告诉JVM如何运行这个JAR
└── org/springframework/boot/loader/
└── ... <-- Spring Boot的"启动加载器",负责启动内嵌的Tomcat并加载你的代码
3. 如何运行?
极其简单。你只需要在安装了Java环境的服务器上,通过一个命令来启动它:
bash
java -jar my-app.jar
执行后,内嵌的Tomcat服务器就会启动,开始监听端口(如8080),你的应用程序就对外提供服务了。它是一个独立的、常驻的进程。
4. PHP世界类比 (一个虚构的场景)
这是最关键的类比。PHP世界里没有与可执行JAR直接对应的东西,但我们可以想象一下:
假设composer
提供了一个新命令composer package --executable
。运行它之后,会发生以下事情:
- 它将你整个Laravel/Symfony项目(
app
,config
,routes
等)打包。 - 它将整个
vendor
目录也打包进去。 - 最神奇的是,它还将一个微型的、预配置好的Nginx和PHP-FPM也打包了进去。
- 最后,它生成了一个单一的可执行文件 ,比如叫做
my-project
。
现在,你要部署,只需要做两件事:
- 把
my-project
这个文件上传到任何一台Linux服务器。 - 在服务器上运行
./my-project
。
瞬间,你的Laravel网站就在http://localhost:8080
上运行起来了。你完全不需要 在服务器上安装Nginx,不需要配置nginx.conf
,也不需要安装和管理PHP-FPM。这个my-project
文件本身就是一个完整的、自给自足的Web应用服务器。
这个虚构的my-project
文件,就是Spring Boot的可执行JAR包。
WAR包 (Web Application ARchive) - 需要"入住酒店"的应用
想象一下你打包好了一个行李箱,里面有你的衣服和个人用品。你不能直接在路边打开行李箱生活,你必须带着这个行李箱,去入住一个已经建好的酒店。酒店提供房间、水电、安保等基础设施,你的行李箱只包含你自己的东西。这就是WAR包。
1. 它是什么?
一个WAR包是为部署到外部的、独立的Java应用服务器(Application Server)而设计的。它包含了Web应用的所有内容(你的代码、依赖、静态资源),但它不包含Web服务器。它期望服务器已经存在。
这种外部的应用服务器也叫Servlet容器 ,最著名的就是Apache Tomcat,其他的还有Jetty, JBoss, WebSphere等。
2. 内部结构 (简化版WAR)
my-app.war
├── WEB-INF/
│ ├── classes/ <-- 你编写的业务代码 (.class文件)
│ ├── lib/ <-- 所有第三方依赖库 (.jar文件)
│ └── web.xml <-- (可选)Web应用部署描述文件,用于配置Servlet等
├── index.html <-- 静态资源,如HTML, CSS, JS
├── images/
└── ...
3. 如何部署?
你不能直接用java -jar
运行WAR包。部署过程是:
- 在服务器上预先安装并启动一个独立的Tomcat服务器。这个Tomcat是一个系统级的服务,长期在后台运行。
- 将
my-app.war
文件复制 到Tomcat的webapps
目录下。 - Tomcat会自动检测到新的WAR文件,然后"解压"并"部署"它。
- 之后,你的应用就可以通过
http://<服务器地址>:8080/my-app
来访问了(/my-app
被称为上下文路径)。
4. PHP世界类比 (非常贴切的场景)
这个类比非常直接:
- 独立的Tomcat服务器 :就等同于你在服务器上已经安装并运行好的Nginx + PHP-FPM服务。这是一个提供PHP网站运行环境的基础设施。
- WAR包 :就等同于你Laravel项目的源代码文件夹的ZIP压缩包。
- 部署过程 :将WAR包复制到Tomcat的
webapps
目录,就完全等同于,你将Laravel项目的ZIP包解压到Nginx配置好的网站根目录(如/var/www/html/my-project
)。Nginx(Tomcat)检测到新文件后,就开始对外提供这个网站(应用)的服务。
如何选择:JAR 还是 WAR?
现在,你已经理解了它们根本性的区别。那么在实际工作中该如何选择?
优先选择JAR包(95%的现代应用场景)
你应该在以下情况下使用JAR包:
- 构建微服务:每个服务都是一个独立的进程,JAR包是完美的选择。
- 云原生和容器化 (Docker/Kubernetes) :将可执行JAR打包成一个Docker镜像极其简单和高效。
FROM openjdk:17
然后COPY my-app.jar /app.jar
再CMD ["java", "-jar", "/app.jar"]
即可。 - 追求简单的开发和部署:单一文件、单一命令,极大地简化了DevOps流程。
- 开发新项目:如果你没有任何历史包袱,直接使用Spring Boot默认的JAR包方式。
一句话总结:如果你想让你的应用"自己管自己",用JAR。
在特定场景下选择WAR包
你需要(或者说,你被迫)在以下情况下使用WAR包:
- 企业传统IT架构要求 :你所在的公司或客户有一个集中管理的应用服务器集群(如Tomcat, WebSphere, JBoss)。运维团队不希望你启动自己的内嵌服务器,而是要求你提供一个标准的WAR包,由他们来统一部署到这个集群中。
- 在单个服务器上运行多个应用:你想在一个Tomcat实例中运行多个Java Web应用,以节省一些内存资源(每个JVM都有基础开销)。但在容器化时代,通过Docker隔离多个JAR应用通常是更好的选择。
- 迁移遗留的Java EE项目:如果你正在维护或改造一个老的、传统的Java Web项目,它原本就是以WAR包形式存在的,保持WAR的格式可能会更容易。
一句话总结:如果你需要把你的应用"交给别人托管",用WAR。
Maven配置 (pom.xml
)
在pom.xml
中切换打包方式非常简单,只需要修改<packaging>
标签:
xml
<packaging>jar</packaging>
xml
<packaging>war</packaging>
注意 :当打包成WAR时,你通常需要告诉Spring Boot,内嵌的Tomcat服务器在运行时将由外部提供,所以在打包时不需要包含它。这通过将依赖的<scope>
设置为provided
来完成:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope> </dependency>
总结对比
特性 | JAR (可执行) | WAR |
---|---|---|
包含服务器? | 是 (内嵌Tomcat等) | 否 |
如何运行? | java -jar my-app.jar (独立进程) |
部署到外部应用服务器 (如Tomcat) |
部署单元 | 一个自包含、可执行的文件 | 一个需要外部环境的Web应用包 |
适用场景 | 微服务、云原生、Docker、现代应用 | 传统企业部署、遗留系统、多应用单实例 |
PHP类比 | 一个自带Nginx+FPM的单一可执行项目文件 (虚构) | 一个部署到现有Nginx+FPM环境的项目代码ZIP包 |
优势 | 简单、独立、可移植、DevOps友好 | 标准化、易于在传统应用服务器中进行管理 |
对于从PHP转到Java的开发者来说,Spring Boot的可执行JAR包模式是更符合现代开发思想、也更容易理解和上手的选择。它摆脱了传统Java EE应用服务器的沉重和复杂,让Java Web开发变得和运行一个脚本一样简单。