深入分析JAR和WAR包的区别 (指南七)

这是一个在Java世界中至关重要,但对初学者(尤其是从PHP背景过来的开发者)来说常常感到困惑的话题。打包成JAR还是WAR,这个选择直接决定了你的应用程序的架构、部署方式和运维模式。

让我们深入思考这个问题,详细讲解两者的区别、适用场景,并用你熟悉的PHP世界进行类比。


核心思想:从"一堆代码文件"到"一个标准化的包裹"

首先,回顾一下我们之前的讨论。PHP的部署本质上是同步源代码 。你将一个包含index.phpapp/vendor/等目录的文件夹部署到服务器上,由一个已经配置好的Web服务器(Nginx/Apache + PHP-FPM)来解释执行这些代码。部署的单元是一个文件夹

Java则不同。Java是编译型语言,我们不部署源代码。我们部署的是一个经过编译、测试、打包后生成的标准化的"包裹",也称为"产物"(Artifact) 。这个包裹就是JARWAR文件。部署的单元是一个文件

JARWAR的本质都是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。运行它之后,会发生以下事情:

  1. 它将你整个Laravel/Symfony项目(app, config, routes等)打包。
  2. 它将整个vendor目录也打包进去。
  3. 最神奇的是,它还将一个微型的、预配置好的Nginx和PHP-FPM也打包了进去。
  4. 最后,它生成了一个单一的可执行文件 ,比如叫做my-project

现在,你要部署,只需要做两件事:

  1. my-project这个文件上传到任何一台Linux服务器。
  2. 在服务器上运行 ./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包。部署过程是:

  1. 在服务器上预先安装并启动一个独立的Tomcat服务器。这个Tomcat是一个系统级的服务,长期在后台运行。
  2. my-app.war文件复制 到Tomcat的webapps目录下。
  3. Tomcat会自动检测到新的WAR文件,然后"解压"并"部署"它。
  4. 之后,你的应用就可以通过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.jarCMD ["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开发变得和运行一个脚本一样简单。

相关推荐
Chen-Edward41 分钟前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
magic334165631 小时前
Springboot整合MinIO文件服务(windows版本)
windows·spring boot·后端·minio·文件对象存储
开心-开心急了1 小时前
Flask入门教程——李辉 第一、二章关键知识梳理(更新一次)
后端·python·flask
掘金码甲哥1 小时前
调试grpc的哼哈二将,你值得拥有
后端
小学鸡!2 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
爱学习的大牛1232 小时前
MVVM 架构 android
android·mvvm
番茄Salad2 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
用户21411832636023 小时前
OpenSpec 实战:用规范驱动开发破解 AI 编程协作难题
后端
Olrookie4 小时前
若依前后端分离版学习笔记(二十)——实现滑块验证码(vue3)
java·前端·笔记·后端·学习·vue·ruoyi
alexhilton4 小时前
理解retain{}的内部机制:Jetpack Compose中基于作用域的状态保存
android·kotlin·android jetpack