乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

maven-war-plugin

本章是"插件"章节的最后一个具体插件讲解。maven-war-plugin 负责将 Web 应用打包为 WAR(Web Application Archive)格式,是 Java Web 项目部署到 Tomcat、Jetty 等 Servlet 容器的必经之路。理解 WAR 包的内部结构和打包过程,是前后端协作部署的基础。


核心机制

一个 WAR 包需要收集三类内容:

  1. artifact dependencies:项目依赖的 JAR(如 Spring MVC、MySQL 驱动)
  2. classes:本项目编译生成的 .class 文件
  3. resources:静态资源(HTML、JS、CSS)和配置文件

这三者共同构成了一个可部署的 Web 应用。

WAR 包的内部结构

一个标准的 WAR 包本质上是一个 ZIP 压缩文件,内部有严格的目录约定:

example.war
├── META-INF/
│   └── MANIFEST.MF          # 清单文件
├── WEB-INF/
│   ├── classes/             # 本项目的 .class 文件和配置文件
│   ├── lib/                 # 第三方依赖 JAR
│   └── web.xml              # Servlet 配置(传统方式)
├── index.html               # 静态资源(可直接访问)
├── css/
├── js/
└── images/

关键规则:

  • WEB-INF/ 下的内容不能被浏览器直接访问,是 Web 应用的安全区域
  • WEB-INF/classes/ 放本项目的编译输出,优先级高于 WEB-INF/lib/ 中的 JAR
  • WEB-INF/lib/ 放所有第三方依赖,Servlet 容器会自动将其加入 classpath

finalName 配置

默认情况下,WAR 包的文件名是 ${artifactId}-${version}.war。例如 employee-web-1.0.0.war。

通过 finalName 可以自定义打包后的文件名:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>3.4.0</version>
    <configuration>
        <finalName>employee-web</finalName>
    </configuration>
</plugin>

这样生成的文件名就是 employee-web.war,去掉了版本号,方便部署脚本固定引用。

打包过程

maven-war-plugin 的打包流程:

  1. 收集 classes:将 target/classes 复制到 WEB-INF/classes/
  2. 收集依赖:将 <dependencies> 中 scope 为 compile 和 runtime 的 JAR 复制到 WEB-INF/lib/
  3. 收集资源:将 src/main/webapp/ 下的静态资源复制到 WAR 根目录
  4. 生成 WAR:按标准结构压缩为 .war 文件

生活类比:搬家打包

想象你要搬家(部署 Web 应用):

  • 你的衣物和证件 = WEB-INF/classes/。这是你个人的东西,最重要,要放在保险箱里(安全区域,外部不能直接访问)。
  • 你买的家具和电器 = WEB-INF/lib/。这些是第三方品牌的产品,你买了使用权,搬家时一起带走。
  • 门口的地毯和门牌 = WAR 根目录的 index.html、css/、js/。这些是门面,访客可以直接看到。

maven-war-plugin 就是那个专业的搬家打包师傅,他知道:证件放保险箱、家具装箱贴标签、门牌挂门口——一切按标准流程来,到了新家(Tomcat)直接拎包入住。


图示

上图展示了 WAR 包的组装逻辑:三类输入资源按固定规则放入 WAR 的标准目录结构中。Servlet 容器(如 Tomcat)解压 WAR 后,正是依赖这个标准结构来加载类、资源和配置。


完整示例

场景

飞翔科技要开发一个员工信息管理的 Web 应用 employee-web。前端黄俪负责页面,后端小崔负责接口,运维李眉要求部署时 WAR 包文件名固定为 employee-web.war,不要带版本号。

操作前:项目状态

当前是普通的 JAR 项目结构:

employee-web/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   └── resources/
│   └── test/
└── target/

pom.xml 的 packaging 是 jar:

<packaging>jar</packaging>

操作步骤:改为 WAR 项目

第一步:修改 packaging 为 war

<packaging>war</packaging>

第二步:添加 servlet-api 依赖(provided,因为容器自带)

<dependencies>
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>6.0.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

第三步:创建 src/main/webapp/ 目录并添加静态资源

employee-web/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   ├── resources/
│   │   └── webapp/              # 新增
│   │       ├── index.html
│   │       ├── css/
│   │       │   └── style.css
│   │       └── WEB-INF/
│   │           └── web.xml
│   └── test/
└── target/

第四步:显式声明 maven-war-plugin,配置 finalName

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.4.0</version>
            <configuration>
                <finalName>employee-web</finalName>
            </configuration>
        </plugin>
    </plugins>
</build>

操作结果:打包前后的目录变化

执行打包:

$ mvn clean package
[INFO] --- maven-war-plugin:3.4.0:war (default-war) @ employee-web ---
[INFO] Packaging webapp
[INFO] Assembling webapp [employee-web] in [target\employee-web]
[INFO] Processing war project
[INFO] Copying webapp resources [src\main\webapp]
[INFO] Webapp assembled in [30 msecs]
[INFO] Building war: target\employee-web.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

打包前 target/ 目录:

target/
├── classes/
│   └── com/feixiang/...
└── generated-sources/

打包后 target/ 目录:

target/
├── classes/
├── employee-web/               # 展开的 WAR 目录(中间产物)
│   ├── index.html
│   ├── css/
│   ├── WEB-INF/
│   │   ├── classes/            # 本项目的 .class
│   │   ├── lib/                # 第三方 JAR
│   │   └── web.xml
│   └── META-INF/
└── employee-web.war            # 最终产物

变化分析:

  • 黄俪的 index.html 和 css/style.css 被复制到了 WAR 根目录,部署后用户可以直接访问
  • 小崔的后端代码编译后进入了 WEB-INF/classes/,Servlet 容器会加载这些类
  • scope=provided 的 servlet-api 没有被打包进 WEB-INF/lib/,因为 Tomcat 自带这个 JAR,避免版本冲突
  • 李眉的部署脚本可以固定引用 employee-web.war,不受版本号变化影响

易错点与常见问题

误区一:packaging 忘了改成 war

错误配置:

<packaging>jar</packaging>

后果:mvn package 会调用 maven-jar-plugin 生成 JAR 文件,而不是 WAR。JAR 文件没有 WEB-INF/ 结构,部署到 Tomcat 时会被拒绝或无法识别。Java Web 项目必须显式声明 <packaging>war</packaging>。

误区二:servlet-api 的 scope 不是 provided

错误配置:

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
    <!-- 漏写 <scope>provided</scope> -->
</dependency>

后果:servlet-api 会被打包进 WEB-INF/lib/。而 Tomcat 本身也自带 servlet-api.jar,运行时会出现类冲突(ClassCastException 或 LinkageError),因为同一个类被两个不同的类加载器加载。Web 容器提供的 API 一律用 provided。

误区三:静态资源放在 src/main/resources

错误认知:"我的 index.html 放在 src/main/resources 里,打包后应该能访问到吧?"

纠正:src/main/resources 的内容会被复制到 WEB-INF/classes/,而 WEB-INF/ 下的内容不能被浏览器直接访问。静态页面、JS、CSS 必须放在 src/main/webapp/ 下,才能出现在 WAR 根目录,供外部访问。


小结

maven-war-plugin 是 Java Web 项目的打包核心,它将编译输出、依赖 JAR 和静态资源按标准 WAR 结构组装为可部署包。finalName 可以简化部署脚本对文件名版本号的处理。记住三个关键规则:packaging 必须是 war、容器 API 必须是 provided、静态资源必须放在 src/main/webapp/。

本章与全局的关系:本章结束了"插件"章节。下一章"继承与聚合"将讲解如何用 parent 和 modules 管理多模块项目,让飞翔科技的多个服务共享统一的配置和依赖版本。

上一页
maven-surefire-plugin