这段代码展示了 Apache Tomcat Maven Plugin(如 tomcat8-maven-plugin)中"部署"和"卸载"类目标的完整设计体系 。它通过多层抽象 + 组合复用 + 注解驱动的方式,实现了高度灵活、可配置、可扩展的远程 Tomcat 操作能力。
我们来逐层拆解理解:
🌐 整体结构概览
text
AbstractWarCatalinaMojo
↓
AbstractDeployMojo ← 定义部署逻辑(war / context / both)
↓
AbstractDeployWarMojo ← 专用于 WAR 文件部署
↓
DeployMojo ← @Mojo(name = "deploy") ------ 绑定到 package 阶段
DeployOnlyMojo ← @Mojo(name = "deploy-only") ------ 不绑定生命周期
↓
RedeployMojo ← 覆盖 isUpdate() → true(自动 undeploy 再 deploy)
RedeployOnlyMojo ← 同上,但不绑定生命周期
UndeployMojo ← 独立实现,用于卸载应用
✅ 所有目标都继承自
AbstractWarCatalinaMojo,因此天然支持:
- 只处理 WAR 项目
- 连接远程 Tomcat Manager
- 多种认证方式
- 日志与错误处理
🔍 核心类详解
1️⃣ AbstractDeployMojo ------ 部署策略的抽象基类
- 核心功能 :根据
mode参数决定部署方式:war:只部署 WAR 文件(默认)context:只部署context.xmlboth:同时部署 WAR + context.xml
java
@Override
public void invokeManager()
throws MojoExecutionException, TomcatManagerException, IOException
{
if ( "war".equals( mode ) )
{
deployWar();
}
else if ( "context".equals( mode ) )
{
deployContext();
}
else if ( "both".equals( mode ) )
{
deployWarAndContext();
}
else
{
throw new MojoExecutionException( messagesProvider.getMessage( "AbstractDeployMojo.unknownMode", mode ) );
}
}
/**
* Deploys the WAR to Tomcat.
*
* @throws org.apache.maven.plugin.MojoExecutionException
* if there was a problem locating the WAR
* @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
* if the Tomcat manager request fails
* @throws java.io.IOException if an i/o error occurs
*/
protected void deployWar()
throws MojoExecutionException, TomcatManagerException, IOException
{
validateWarFile();
getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingWar", getDeployedURL() ) );
URL warURL = getWarFile().toURL();
log( getManager().deploy( getPath(), warURL, isUpdate(), getTag() ).getHttpResponseBody() );
}
/**
* Deploys the context XML file to Tomcat.
*
* @throws org.apache.maven.plugin.MojoExecutionException
* if there was a problem locating the context XML file
* @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
* if the Tomcat manager request fails
* @throws java.io.IOException if an i/o error occurs
*/
protected void deployContext()
throws MojoExecutionException, TomcatManagerException, IOException
{
validateContextFile();
getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingContext", getDeployedURL() ) );
URL contextURL = getContextFile().toURL();
log( getManager().deployContext( getPath(), contextURL, isUpdate(), getTag() ).getHttpResponseBody() );
}
/**
* Deploys the WAR and context XML file to Tomcat.
*
* @throws org.apache.maven.plugin.MojoExecutionException
* if there was a problem locating either the WAR or the context XML file
* @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
* if the Tomcat manager request fails
* @throws java.io.IOException if an i/o error occurs
*/
protected void deployWarAndContext()
throws MojoExecutionException, TomcatManagerException, IOException
{
validateWarFile();
validateContextFile();
getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingWarContext", getDeployedURL() ) );
URL warURL = getWarFile().toURL();
URL contextURL = getContextFile().toURL();
TomcatManagerResponse tomcatResponse = getManager().deployContext( getPath(), contextURL, warURL, isUpdate(), getTag() );
checkTomcatResponse( tomcatResponse );
log( tomcatResponse.getHttpResponseBody() );
}
-
关键方法:
javaprotected abstract File getWarFile(); // 子类提供 WAR 路径 protected abstract void validateWarFile(); // 子类校验 WAR 是否存在 -
参数:
mode:部署模式contextFile:上下文文件路径(默认${project.build.directory}/.../META-INF/context.xml)update:是否先 undeploy 再 deploy(对应 Tomcat 的?update=true)tag:用于标记部署版本(高级功能)
💡 它不关心 WAR 是文件还是目录,把具体实现留给子类。
2️⃣ AbstractDeployWarMojo ------ WAR 文件部署的具体实现
-
覆盖父类方法:
java@Override protected File getWarFile() { return warFile; // 来自参数,默认是 target/${artifactId}.war } @Override protected void validateWarFile() { if (!warFile.exists()) throw ...; } -
优化
deployWar():- 调用
getManager().deploy(..., warFile, ..., warFile.length()) - 显式传入文件大小 → 支持更高效的 HTTP 上传(避免先读入内存)
- 调用
java
/**
* {@inheritDoc}
*/
@Override
protected void deployWar()
throws MojoExecutionException, TomcatManagerException, IOException
{
validateWarFile();
getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingWar", getDeployedURL() ) );
TomcatManagerResponse tomcatManagerResponse =
getManager().deploy( getPath(), warFile, isUpdate(), getTag(), warFile.length() );
checkTomcatResponse( tomcatManagerResponse );
getLog().info( "tomcatManager status code:" + tomcatManagerResponse.getStatusCode() + ", ReasonPhrase:"
+ tomcatManagerResponse.getReasonPhrase() );
log( tomcatManagerResponse.getHttpResponseBody() );
}
✅ 这是 最常用 的部署方式:直接上传一个
.war文件到 Tomcat。
3️⃣ DeployMojo vs DeployOnlyMojo ------ 生命周期绑定差异
| 类 | 注解 | 行为 |
|---|---|---|
DeployMojo |
@Execute(phase = LifecyclePhase.PACKAGE) |
执行前自动触发 package 阶段(确保 WAR 已生成) |
DeployOnlyMojo |
无 @Execute |
不触发任何生命周期,假定 WAR 已存在 |
使用场景:
bash
# 自动打包并部署
mvn tomcat8:deploy
# 假设 WAR 已存在,直接部署(CI/CD 中常用)
mvn tomcat8:deploy-only
🔹
@Execute是 Maven 插件的"前置任务"机制。
4️⃣ RedeployMojo / RedeployOnlyMojo ------ 便捷别名
-
核心 trick :重写
isUpdate()返回truejava@Override protected boolean isUpdate() { return true; // 等价于 -Dmaven.tomcat.update=true } -
效果 :Tomcat 会先 undeploy 同 path 的应用,再 deploy 新的 → 避免"already exists"错误
✅ 用户无需记参数,直接用
redeploy更直观。
5️⃣ UndeployMojo ------ 独立卸载逻辑
-
简单直接 :调用
manager.undeploy(path) -
容错设计 :
javaif (failOnError) throw exception; else log.warn("Failed to undeploy, but continuing..."); -
适用场景:清理环境、滚动部署前移除旧版本
🧩 设计亮点总结
| 特性 | 说明 |
|---|---|
| 分层抽象 | 从"是否 WAR 项目" → "如何连接 Tomcat" → "部署什么" → "如何部署",职责清晰 |
| 策略模式 | mode=war/context/both 允许不同部署策略 |
| 模板方法 | invokeManager() 调用子类实现的 deployXxx() |
| 生命周期集成 | @Execute(phase = PACKAGE) 自动构建 WAR |
| 用户友好 | 提供 deploy / redeploy / deploy-only 等语义化目标 |
| 安全容错 | failOnError 控制卸载失败是否中断构建 |
🛠️ 实际使用示例
场景 1:标准部署(自动打包)
bash
mvn tomcat8:deploy \
-Dtomcat.username=admin \
-Dtomcat.password=secret \
-Dmaven.tomcat.url=http://prod-server:8080/manager/text
场景 2:重新部署(覆盖已有应用)
bash
mvn tomcat8:redeploy
# 等价于 mvn tomcat8:deploy -Dmaven.tomcat.update=true
场景 3:仅卸载(不中断构建)
bash
mvn tomcat8:undeploy -Dmaven.tomcat.failOnError=false
✅ 总结一句话
这套代码通过"抽象基类 + 具体实现 + 注解元数据"的组合,将 Tomcat 部署/卸载操作封装成一组语义清晰、配置灵活、安全可靠的 Maven 目标(goals),既满足日常开发需求,也适配自动化部署场景。
它是 Maven 插件设计的最佳实践范例:高内聚、低耦合、易扩展、用户友好。