使用assembly插件对boot项目打zip和war部署包

news2025/1/19 14:14:33

目录

一、maven-assembly-plugin插件的简单使用

1、什么是assembly?

2. 常见的maven插件

3、如何使用? 

二、如何通过assembly打不同的包

三、boot项目如何转成war包部署


背景:之前项目上已经使用了assembly对多个boot项目分别打zip包且分别部署,这次资源瘦身,某些用户需要将他们打成war包,都放在一个web容器中运行。

本篇文章涉及如下三点内容:

  • maven-assembly-plugin插件的简单使用
  • 使用assembly插件根据参数打不同的部署包
  • boot项目如何转成war包部署到web容器

一、maven-assembly-plugin插件的简单使用

1、什么是assembly?

简单的说,maven-assembly-plugin 就是用来帮助打包用的,比如说打出一个什么类型的包,包里包括哪些内容等等。

2. 常见的maven插件

maven插件是在生命周期中某些阶段执行的任务。一个插件完成一项功能。以下介绍几种常见的插件。
如对于打包来说,有多种插件选择。最常见的有以下3个:

pluginfunction
maven-jar-pluginmaven 默认打包插件,用来创建 project jar
maven-shade-plugin用来打可执行包,executable(fat) jar
maven-assembly-plugin支持定制化打包方式,例如 apache 项目的打包方式

3、如何使用? 

使用assembly,需要在pom.xml文件中添加如下配置:

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.2-beta-5</version>
                <configuration>
                    <!--包的名称-->
                    <finalName>BootDemo</finalName>
                    <!--打包的名称是否拼接assembly.id-->
                    <appendAssemblyId>true</appendAssemblyId>
                    <descriptors>
                        <descriptor>src/main/assembly/assembly.xml</descriptor>
                    </descriptors>
                </configuration>
                <!-- 添加此项后,可直接使用mvn package | mvn install -->
                <!-- 不添加此项,需使用mvn package assembly:single|mvn package assembly:assembly -->
                <executions>
                    <execution>
                        <!--名字任意 -->
                        <id>make-assembly</id>
                        <!-- 绑定到package生命周期阶段上 -->
                        <phase>package</phase>
                        <goals>
                            <!-- 只运行一次 -->
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

接着在对应的路径下添加assembly.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<assembly>
    <!--id 标识符,添加到生成文件名称的后缀符-->
    <id>assembly_id</id>
    <!--支持的打包格式有zip、tar、tar.gz (or tgz)、tar.bz2 (or tbz2)、jar、dir、war,可以同时指定多个打包格式-->
    <formats>
        <format>zip</format>
    </formats>
    <!--默认为true。指定打的包是否包含打包层目录(比如finalName是prefix,当值为true,所有文件被放在包内的prefix目录下,否则直接放在包的根目录下-->
    <includeBaseDirectory>true</includeBaseDirectory>
    <!--定制工程依赖 jar 包的打包方式-->
    <dependencySets>
        <dependencySet>
            <!--指定包依赖目录,该目录是相对于根目录-->
            <outputDirectory>lib</outputDirectory>
            <scope>runtime</scope>
        </dependencySet>
    </dependencySets>

    <!--定制工程下其它文件的打包方式-->
    <fileSets>
        <fileSet>
            <!--原文件目录-->
            <directory>src/main/bin</directory>
            <!--打包的目录-->
            <outputDirectory>/bin</outputDirectory>
            <includes>
                <include>*.sh</include>
            </includes>
            <!--打包文件权限-->
            <fileMode>0755</fileMode>
        </fileSet>
        <fileSet>
            <directory>src/main/resources</directory>
            <outputDirectory>/conf</outputDirectory>
            <fileMode>0755</fileMode>
        </fileSet>

    </fileSets>

</assembly>

测试用的过程结构:

经过以上配置,我们就可以通过

mvn package assembly:assembly 或 mvn clean install 命令打出一个zip包了

二、如何通过assembly打不同的包

其实就是通过添加多个assemblyy.xmll来实现的,具体修改如下:

pom.xml文件添加如下配置:

<!--配置不同的打包配置,如果不指定参数。默认一最后的profile配置为准,即,如下配置,默认打zip-->
    <profiles>
        <!--打war包的配置-->
        <profile>
            <id>war</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <property>
                    <name>type</name>
                    <value>war</value>
                </property>
            </activation>
            <properties>
                <type>war</type>
            </properties>

            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-assembly-plugin</artifactId>
                        <configuration>
                            <descriptors>src/main/assembly/assembly_war.xml</descriptors>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

        <!--打zip包的配置-->
        <profile>
            <id>zip</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <property>
                    <name>type</name>
                    <value>zip</value>
                </property>
            </activation>
            <properties>
                <type>zip</type>
            </properties>

            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-assembly-plugin</artifactId>
                        <configuration>
                            <descriptors>src/main/assembly/assembly.xml</descriptors>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

再添加一个assembly_war.xml

<?xml version="1.0" encoding="UTF-8" ?>
<assembly>
    <id>assembly</id>
    <formats>
        <format>war</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <directory>{basedir}/target/classes</directory>
            <fileMode>0755</fileMode>
            <outputDirectory>WEB-INF/classes</outputDirectory>
        </fileSet>

    </fileSets>
    <dependencySets>
        <dependencySet>
            <outputDirectory>WEB-INF/lib</outputDirectory>
            <useProjectArtifact>false</useProjectArtifact>
        </dependencySet>
    </dependencySets>
</assembly>

这样我们就可以通过mvn clean install -P war|zip来打不同的包了

三、boot项目如何转成war包部署

我们知道boot的项目会自动生成一个启动类,并通过该启动类启动

@SpringBootApplication
public class BootdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootdemoApplication.class, args);
    }

}

跟踪SpringApplication.run方法发现,核心方法为:org.springframework.boot.SpringApplication#run(java.lang.String...)

public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
            //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            //根据webEnvironment的值来决定创建何种类型的ApplicationContext对象
            //如果是web环境,则创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
            //否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            //主要是调用所有初始化类的 initialize 方法
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            //初始化 Spring 容器
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }

            listeners.started(context, timeTakenToStartup);
            //调用 ApplicationRunner 或者 CommandLineRunner 的运行方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

那么问题来了,转成war部署后,如何由web容器拉起这个过程呢? 其实boot已经给我们考虑过了

,boot提供了一个SpringBootServletInitializer类,修改如下:

@SpringBootApplication
    public class BootdemoApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(BootdemoApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(BootdemoApplication.class);
    }

}

如果启动类无法继承(如启动类已经有了父类),可以新建一个类继承SpringBootServletInitializer,并重写configure即可:

    public class ProjectServletInitializer extends SpringBootServletInitializer {

     @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(BootdemoApplication.class);
    }

}

 跟踪源码发现,最终还是在web容器启动的时候调用到了org.springframework.boot.SpringApplication#run(java.lang.String...)

大致原理如下:

1、spring-boot的SpringBootServletInitializer类实现了spring-web的 WebApplicationInitializer接口

public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
    protected Log logger;
    private boolean registerErrorPageFilter = true;

    public SpringBootServletInitializer() {
    }

    protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
        this.registerErrorPageFilter = registerErrorPageFilter;
    }

    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.setAttribute("logging.register-shutdown-hook", false);
        this.logger = LogFactory.getLog(this.getClass());
        //重点方法,这里最终会调用到SpringApplication#run
        WebApplicationContext rootApplicationContext = this.createRootApplicationContext(servletContext);
        if (rootApplicationContext != null) {
            servletContext.addListener(new SpringBootServletInitializer.SpringBootContextLoaderListener(rootApplicationContext, servletContext));
        } else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
        }

    }
//...
}

2、和WebApplicationInitializer类同一目录下有一个SpringServletContainerInitializer类,该类实现了javax.servlet-api的ServletContainerInitializer接口,并将WebApplicationInitializer类通过注解@HandlesTypes传递给了onStartup方法的第一个参数

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = Collections.emptyList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            initializers = new ArrayList(webAppInitializerClasses.size());
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        ((List)initializers).add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (((List)initializers).isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(((List)initializers).size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort((List)initializers);
            var4 = ((List)initializers).iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                //重点
                initializer.onStartup(servletContext);
            }

        }
    }
}

ServletContainerInitializer 是 Servlet 3.0 新增的一个接口,主要用于在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等。

每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类(即SPI机制),那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

 这样,就可以将boot项目通过assembly插件打成war包并部署到web容器了~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434869.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB应用笔记

其他 1、NaN值 MATLAB判断数据是否为NaN可以直接使用函数&#xff1a;isnan() 三、数据分析 1、相关性 均值、方差、协方差、标准差、相关系数 mean() %均值 nanmean()%去除NAN值求均值 var() %方差 cov() %协方差 std() %标准差 corrcoef(B,b) %R 相关系数plot()…

[ 云原生 | Docker ] 构建高可用性的 SQL Server:Docker 容器下的主从同步实现指南

文章目录 一、前言二、SQL Server 主从同步的原理介绍三、具体的搭建过程3.1 准备工作3.1.1 卸载旧版本&#xff08;如果有&#xff0c;可选&#xff0c;非必须&#xff09;3.1.2 安装 Docker3.1.3 验证本地 Docker 是否安装成功 3.2 创建 Docker 网络3.3 创建主从节点的 SQL S…

部署环境从docker swarm迁移到k8s后kie-server的发布方式变化(二)

正如后来的考虑, 如果外接maven私库照理说是ok的, 这样去掉volume的设计整个流程更加的自动化标准化. 开搞 第一步先部署一个nexus yaml文件如下: apiVersion: v1 kind: Namespace metadata:name: nexus---apiVersion: v1 kind: Service metadata:labels:k8s-app: nexusname…

【数据结构与算法】八大排序

[数据结构与算法]八大排序 数据结构与算法-八大排序排序的概念及其应用排序的概念排序的应用 常见的排序算法实现常见的排序算法插入排序直接插入排序希尔排序(缩小增量排序)希尔排序的时间复杂度希尔排序对插入排序的优化效果 选择排序直接选择排序堆排序向上调整建堆&#xf…

【C语言】从n个字符串中匹配查找abc字符串个数

目录 前言知识点重温1、scanf和scanf_s区别2、指针3、char、char*使用查找字符串收尾前言 这是小5聊的《C语言知识点例子》系列的第四篇文章。 在软件行业已经有快十年,技术虽然一般般,但是足够应付和解决编程入门的相关问题! 都说十年磨一剑,积累到一定经验,是时候发挥自…

数据库基础篇 《1. 概述》

目录 1. 为什么要使用数据库 2. 数据库与数据库管理系统 2.1 数据库的相关概念 2.2 数据库与数据库管理系统的关系 2.3 常见的数据库管理系统排名(DBMS) 2.4 常见的数据库介绍 3. MySQL介绍 3.1 概述 3.2 MySQL发展史重大事件 4. RDBMS 与 非RDBMS 4.1 关系型数据库…

Delphi Web Server 流程分析

通过向导 "Web Server Application" (选择 "Stand-alone GUI Application") 创建一个 WebServer Demo。 主单元代码: ...... private FServer: TIdHTTPWebBrokerBridge; procedure StartServer; ............. Delphi的网络组件是基于INDY的&a…

项目四:无极调光台灯

项目四&#xff1a;无极调光台灯 文章目录 项目四&#xff1a;无极调光台灯一、导入(5分钟&#xff09;学习目的 二、新授(65分钟)1.预展示结果(5分钟)2.本节课所用的软硬件(5分钟)3.硬件介绍(5分钟)4.图形化块介绍(10分钟)5.单个模块的简单使用(10分钟)6.无极调光台灯编程逻辑…

SQLServer的内存管理架构

内存管理架构说明 一、Windows的虚拟内存管理器二、SQL Server 内存体系结构2.1、传统&#xff08;虚拟&#xff09;内存2.2、地址窗口扩展 &#xff08;AWE&#xff09; 内存 三、从 SQL Server 2012 &#xff08;11.x&#xff09; 开始发生的改变3.1、对内存管理的更改3.2、对…

【网络原理】TCP/IP协议

目录 1.应用层 2.传输层&#xff08;核心问题&#xff09; 2.1 UDP协议 2.1.2 UDP的特点 2.1.3 基于UDP的应用层协议 2.2 TCP协议&#xff08;重点内容&#xff09; 2.2.1 TCP/IP 协议含义 2.2.2 TCP协议端格式&#xff1a; 2.2.3 TCP的特点 2.3 TCP原理 2.4 确认应…

Fork/Join优化mybatis-plus批量插入性能

最近在项目开发中&#xff0c;遇到需要一次性保存100万数据到数据库中。想到以下几种实现方式&#xff1a; 第一种方案&#xff1a;在mapper文件中&#xff0c;实现批量插入动态SQL语句&#xff0c;采用insert into table_name values(?,?,?,),(?,?,?)拼接SQL方式。 &l…

HTTP中ETag语法及使用实战详解

1.1 ETag 是什么 ETag&#xff08;Entity Tag&#xff09;是万维网协议 HTTP 的一部分。它是 HTTP 协议提供的若干机制中的一种 Web 缓存验证机制&#xff0c;并且允许客户端进行缓存协商。这使得缓存变得更加高效&#xff0c;而且节省带宽。如果资源的内容没有发生改变&#x…

电脑网速慢怎么解决?4个方法有效提升电脑网速!

案例&#xff1a;电脑网速慢怎么解决 【谁懂啊&#xff01;我的电脑网速太慢了&#xff01;总是上不了网&#xff0c;打开浏览器也是一直在转圈圈&#xff01;太折磨了&#xff01;这是为什么呢&#xff1f;谁能帮帮我呀&#xff01;】 随着互联网的发展和普及&#xff0c;电…

继续学c++

由于c里面有很多和c语言很像的东西&#xff0c;这里就来总结一点不像的或者要注意的&#xff0c;或者是我已经快忘记的&#xff1b; 先来一个浮点型也就是实型类型的总结&#xff1b; 知道浮点型有这两个类型&#xff1a;float和double型&#xff1b; 然后float型占四个字节…

To B第六年,腾讯过分温柔

腾讯做2B&#xff0c;方向是正确的&#xff0c;初心是果决的&#xff0c;行动是温柔的&#xff0c;事实是掉队的。 2018年&#xff0c;率先打出“互联网的下半场属于产业互联网”的大旗&#xff0c;宣布“拥抱产业互联网”&#xff0c;腾讯发力To B业务&#xff0c;绝对是有先发…

HTB-Jarvis

HTB-Jarvis 信息收集80端口 www-data(sqlmap)www-data(myPhpAdmin)www-data -> pepperpepper -> root 信息收集 80端口 目录扫描 我啥也没干咋就被ban了&#xff0c;可能是gobuster流量太大被逮住了。 老老实实等90秒&#xff0c;先从已有的目录收集信息。 phpMyAdmin 4…

X 态及基于 VCS 的 X-Propagation 检测

&#x1f525;点击查看精选 IC 技能树系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#…

Lightroom Classic2022图文安装教程

Lightroom Classic是一款专业的数字照片处理软件&#xff0c;具有数字照片编辑、照片整理和管理、批量处理、智能预览、输出等特点。 该软件适用于摄影师和数字照片后期处理爱好者&#xff0c;可以帮助用户提高处理效率和照片质量。 Lightroom Classic是Adobe公司推出的系列软…

ai智能写作软件哪个好-ai智能写作免费

人工智能自动写作软件 人工智能自动写作软件是如今数字营销领域中备受瞩目的一种工具。无论是网络文章、博客、报告、新闻稿或者其他一些营销内容&#xff0c;人工智能自动写作软件可以以相当高的速度和质量将其生成&#xff0c;从而释放人类营销人员的时间和精力。 尽管自动写…

深度学习实战案例:基于 AutoRec 构建电影推荐系统( 附 PyTorch 版代码)

文章目录 技术交流前言AutoRec 模型介绍损失函数基于 AutoRec 的推荐过程实验对比消融实验代码实践总结参考 本文要介绍的 AutoRec 模型是由澳大利亚国立大学在2015年提出的&#xff0c;它将自编码器(AutoEncoder)的思想与协同过滤(Collaborative Filter)的思想结合起来&#x…