Tomcat Maven插件部署与卸载的完整架构设计

 更新时间:2026年02月10日 09:38:17   作者:lang20150928  
文章详细介绍了Apache Tomcat Maven Plugin中的部署和卸载功能,通过多层抽象、组合复用和注解驱动的设计,实现了灵活、可配置的远程Tomcat操作,通过代码展示了Apache Tomcat Maven Plugin(如 tomcat8-maven-plugin)中部署和卸载类目标的设计体系,感兴趣的朋友一起看看吧

这段代码展示了 Apache Tomcat Maven Plugin(如 tomcat8-maven-plugin)中“部署”和“卸载”类目标的完整设计体系。它通过多层抽象 + 组合复用 + 注解驱动的方式,实现了高度灵活、可配置、可扩展的远程 Tomcat 操作能力。

我们来逐层拆解理解:

🌐 整体结构概览

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.xml
    • both:同时部署 WAR + context.xml
@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() );
    }

关键方法

protected 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 文件部署的具体实现

覆盖父类方法

@Override
protected File getWarFile() {
    return warFile; // 来自参数,默认是 target/${artifactId}.war
}
@Override
protected void validateWarFile() {
    if (!warFile.exists()) throw ...;
}
  • 优化 deployWar()
  • 调用 getManager().deploy(..., warFile, ..., warFile.length())

显式传入文件大小 → 支持更高效的 HTTP 上传(避免先读入内存)

/**
     * {@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️⃣DeployMojovsDeployOnlyMojo——生命周期绑定差异

注解行为
DeployMojo@Execute(phase = LifecyclePhase.PACKAGE)执行前自动触发 package 阶段(确保 WAR 已生成)
DeployOnlyMojo@Execute不触发任何生命周期,假定 WAR 已存在

使用场景:

# 自动打包并部署
mvn tomcat8:deploy
# 假设 WAR 已存在,直接部署(CI/CD 中常用)
mvn tomcat8:deploy-only

🔹 @Execute 是 Maven 插件的“前置任务”机制。

4️⃣RedeployMojo/RedeployOnlyMojo——便捷别名

核心 trick:重写 isUpdate() 返回 true

@Override
protected boolean isUpdate() {
    return true; // 等价于 -Dmaven.tomcat.update=true
}

效果:Tomcat 会先 undeploy 同 path 的应用,再 deploy 新的 → 避免“already exists”错误

✅ 用户无需记参数,直接用 redeploy 更直观。

5️⃣UndeployMojo——独立卸载逻辑

简单直接:调用 manager.undeploy(path)

容错设计

if (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:标准部署(自动打包)

mvn tomcat8:deploy \
  -Dtomcat.username=admin \
  -Dtomcat.password=secret \
  -Dmaven.tomcat.url=http://prod-server:8080/manager/text

场景 2:重新部署(覆盖已有应用)

mvn tomcat8:redeploy
# 等价于 mvn tomcat8:deploy -Dmaven.tomcat.update=true

场景 3:仅卸载(不中断构建)

mvn tomcat8:undeploy -Dmaven.tomcat.failOnError=false

✅ 总结一句话

这套代码通过“抽象基类 + 具体实现 + 注解元数据”的组合,将 Tomcat 部署/卸载操作封装成一组语义清晰、配置灵活、安全可靠的 Maven 目标(goals),既满足日常开发需求,也适配自动化部署场景。

它是 Maven 插件设计的最佳实践范例:高内聚、低耦合、易扩展、用户友好。

到此这篇关于Tomcat Maven插件部署与卸载的完整架构设计的文章就介绍到这了,更多相关Tomcat Maven插件部署与卸载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • tomcat何时写回响应数据报的详析

    tomcat何时写回响应数据报的详析

    这篇文章主要给大家介绍了关于tomcat是何时写回响应数据报的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用tomcat具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • Java Tomcat 启动闪退问题解决集

    Java Tomcat 启动闪退问题解决集

    Tomcat 启动时出现黑屏一闪而过的现象原因有很多,这篇文章就详细介绍了tomcat启动闪退问题的一些解决方法,感兴趣的同学可以仔细阅读
    2023-03-03
  • 如何设置Tomcat的默认端口(图文)

    如何设置Tomcat的默认端口(图文)

    Tomcat安装时默认的端口设置的是8080,而http协议的默认端口是80,所以测试Tomcat时需要输入的网址为“http://localhost:8080”,若把Tomcat的端口设置为80,则直接输入“http://localhost”就能显示Tomcat默认主页,下面我们来分享下,方便需要的朋友
    2014-06-06
  • tomcat如何更改默认ROOT目录

    tomcat如何更改默认ROOT目录

    这篇文章主要介绍了tomcat如何更改默认ROOT目录问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • tomcat websocket WsFrameServer内存溢出的问题解决

    tomcat websocket WsFrameServer内存溢出的问题解决

    本文主要介绍了tomcat websocket WsFrameServer内存溢出的问题解决,文中通过图文示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12
  • Tomcat目录结构详解

    Tomcat目录结构详解

    Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,学习tomcat目录结构十分有必要,今天小编给大家带来了详细教程,感兴趣的朋友一起看看吧
    2019-11-11
  • Tomcat与Netty的使用比较

    Tomcat与Netty的使用比较

    Tomcat是一个开源的Java Web应用服务器,支持HTTP和AJP协议,适用于中小型系统和并发访问用户不是很多的场合,Netty是一个基于NIO的Java框架,提供异步的事件驱动网络应用程序框架和工具,支持多种协议,适用于需要处理大量并发连接和高吞吐量的场景
    2026-01-01
  • Tomcat的下载安装与使用教程

    Tomcat的下载安装与使用教程

    本文介绍了Tomcat的下载、安装和使用方法,包括在本机和云服务器上部署Tomcat的过程,以及解决启动失败问题的方法
    2025-03-03
  • 图解Linux下安装Tomcat服务器

    图解Linux下安装Tomcat服务器

    这篇文章主要以图文结合的方式为大家详细介绍了Linux下安装Tomcat服务器的详细过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Tomcat核心组件及应用架构详解

    Tomcat核心组件及应用架构详解

    众所周知Tomcat 就是一个 Servlet 容器,为了方便使用,他们具有http服务器的功能,所以Tomcat 就是一个“HTTP 服务器 + Servlet 容器”,我们也叫它们 Web 容器,今天重点给大家普及Tomcat核心组件及应用架构相关知识,感兴趣的朋友一起了解下吧
    2021-05-05

最新评论