Apache Maven Assembly自定义打包插件的使用

时间:2021-04-18 00:36:28

前言

  1. 本文主要记录在SpringBoot项目中使用Apache Maven Assembly插件进行打包的相关内容;
  2. 官网说明:https://maven.apache.org/plugins/maven-assembly-plugin/

概述

  • 是什么:Apache Maven Assembly是Maven的程序集插件使开发人员能够将项目输出合并到单个可分发的存档中,该存档还包含依赖项、模块、站点文档和其他文件。
  • 作用:可以实现自定义打包,从而实现打包项目可以外挂yml配置文件,提供shell运维脚本,大大降低运维成本,比较适用于小规模的SpringBoot项目(大规模的项目推荐docker容器部署)

配置说明

  1. 引入插件(pom文件中)

    <?xml version="1.0" encoding="UTF-8"?>
    <project>
      ...
      <build>
          <plugins>
              <!--springboot项目打包插件-->
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
                  <version>2.3.7.RELEASE</version>
                  <configuration>
                      <mainClass>com.alan.SpringBootMain</mainClass>
                  </configuration>
                  <executions>
                      <execution>
                          <goals>
                              <goal>repackage</goal>
                          </goals>
                      </execution>
                  </executions>
              </plugin>
              <!--maven自定义打包插件-->
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-assembly-plugin</artifactId>
                  <version>3.5.0</version>
                  <configuration>
                      <descriptors>
                          <!--具体的配置文件-->
                          <descriptor>src/main/assembly/assembly.xml</descriptor>
                      </descriptors>
                  </configuration>
                  <executions>
                      <execution>
                          <id>make-assembly</id>
                          <!--绑定到maven操作类型上-->
                          <phase>package</phase>
                          <!--运行一次-->
                          <goals>
                              <goal>single</goal>
                          </goals>
                      </execution>
                  </executions>
              </plugin>
          </plugins>
      </build>
    </project>
    
  2. 配置assembly文件(在pom中设定的位置下创建,我这里是src/main/assembly/assembly.xml)

    <assembly>
        <!--
            必须写,否则打包时会有 assembly ID must be present and non-empty 错误
            这个名字最终会追加到打包的名字的末尾,如项目的名字为 hangge-test-0.0.1-SNAPSHOT,
            则最终生成的包名为 hangge-test-0.0.1-SNAPSHOT-bin.tar.gz
        -->
        <id>bin</id>
        <!-- 打包的类型,如果有N个,将会打N个类型的包 -->
        <formats>
            <format>tar.gz</format>
            <!--<format>zip</format>-->
        </formats>
        <includeBaseDirectory>true</includeBaseDirectory>
    
        <!--第三方依赖设置-->
        <dependencySets>
            <dependencySet>
                <!--使用项目中的artifact,第三方包打包进tar.gz文件的lib目录下-->
                <useProjectArtifact>true</useProjectArtifact>
                <outputDirectory>lib</outputDirectory>
            </dependencySet>
        </dependencySets>
        <!--文件设置-->
        <fileSets>
            <!--
                0755->即用户具有读/写/执行权限,组用户和其它用户具有读写权限;
                0644->即用户具有读写权限,组用户和其它用户具有只读权限;
            -->
            <!-- 将src/main/assembly/bin目录下的所有文件输出到打包后的bin目录中 -->
            <fileSet>
                <directory>src/main/assembly/bin</directory>
                <outputDirectory>bin</outputDirectory>
                <fileMode>0755</fileMode>
                <!--如果是脚本,一定要改为unix.如果是在windows上面编码,会出现dos编写问题-->
                <lineEnding>unix</lineEnding>
                <filtered>true</filtered><!-- 是否进行属性替换 -->
            </fileSet>
            <!-- 将src/main/assembly/config目录下的所有文件输出到打包后的config目录中 -->
            <fileSet>
                <directory>src/main/assembly/config</directory>
                <outputDirectory>config</outputDirectory>
                <fileMode>0644</fileMode>
            </fileSet>
            <!-- 将src/main/resources下配置文件打包到config目录 -->
            <fileSet>
                <directory>src/main/resources</directory>
                <outputDirectory>/config</outputDirectory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                    <include>**/*.yml</include>
                </includes>
                <filtered>true</filtered><!-- 是否进行属性替换 -->
            </fileSet>
            <!-- 将项目启动jar打包到lib目录中 -->
            <fileSet>
                <directory>target</directory>
                <outputDirectory>lib</outputDirectory>
                <includes>
                    <include>*.jar</include>
                </includes>
            </fileSet>
            <!-- 将项目说明文档打包到docs目录中 -->
            <fileSet>
                <directory>.</directory>
                <outputDirectory>docs</outputDirectory>
                <includes>
                    <include>*.md</include>
                </includes>
                <fileMode>0644</fileMode>
            </fileSet>
            <fileSet>
                <directory>docs</directory>
                <outputDirectory>docs</outputDirectory>
                <fileMode>0644</fileMode>
            </fileSet>
            <fileSet>
                <directory>src/main/assembly/docs</directory>
                <outputDirectory>docs</outputDirectory>
                <fileMode>0644</fileMode>
            </fileSet>
        </fileSets>
    </assembly>
    
  3. 编写运维shell脚本(在assembly中设定的位置下创建,我这里是src/main/assembly/bin)

    1. start.sh
      #!/bin/bash
      # 项目名称
      SERVER_NAME="${project.artifactId}"
      
      # jar名称
      JAR_NAME="${project.build.finalName}.jar"
      
      # 进入bin目录
      cd `dirname $0`
      # bin目录绝对路径
      BIN_DIR=`pwd`
      # 返回到上一级项目根目录路径
      cd ..
      # 打印项目根目录绝对路径
      # `pwd` 执行系统命令并获得结果
      DEPLOY_DIR=`pwd`
      
      # 外部配置文件绝对目录,如果是目录需要/结尾,也可以直接指定文件
      # 如果指定的是目录,spring则会读取目录中的所有配置文件
      CONF_DIR=$DEPLOY_DIR/config
      # SERVER_PORT=`sed '/server.port/!d;s/.*=//' config/application.properties | tr -d '\r'`
      # 获取应用的端口号
      SERVER_PORT=`sed -nr '/port: [0-9]+/ s/.*port: +([0-9]+).*/\1/p' config/application.yml`
      
      PIDS=`ps -f | grep java | grep "$CONF_DIR" |awk '{print $2}'`
      if [ "$1" = "status" ]; then
          if [ -n "$PIDS" ]; then
              echo "The $SERVER_NAME is running...!"
              echo "PID: $PIDS"
              exit 0
          else
              echo "The $SERVER_NAME is stopped"
              exit 0
          fi
      fi
      
      if [ -n "$PIDS" ]; then
          echo "ERROR: The $SERVER_NAME already started!"
          echo "PID: $PIDS"
          exit 1
      fi
      
      if [ -n "$SERVER_PORT" ]; then
          SERVER_PORT_COUNT=`netstat -tln | grep $SERVER_PORT | wc -l`
          if [ $SERVER_PORT_COUNT -gt 0 ]; then
              echo "ERROR: The $SERVER_NAME port $SERVER_PORT already used!"
              exit 1
          fi
      fi
      
      # 项目日志输出绝对路径
      LOGS_DIR=$DEPLOY_DIR/logs
      # 如果logs文件夹不存在,则创建文件夹
      if [ ! -d $LOGS_DIR ]; then
          mkdir $LOGS_DIR
      fi
      STDOUT_FILE=$LOGS_DIR/catalina.log
      
      # JVM Configuration
      JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
      JAVA_DEBUG_OPTS=""
      if [ "$1" = "debug" ]; then
          JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
      fi
      
      JAVA_JMX_OPTS=""
      if [ "$1" = "jmx" ]; then
          JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false "
      fi
      
      JAVA_MEM_OPTS=""
      BITS=`java -version 2>&1 | grep -i 64-bit`
      if [ -n "$BITS" ]; then
          JAVA_MEM_OPTS=" -server -Xmx512m -Xms512m -XX:PermSize=128m -XX:+UseG1GC "
      else
          JAVA_MEM_OPTS=" -server -Xms512m -Xmx512m -XX:PermSize=128m -XX:+UseParallelGC "
      fi
      
      # 加载外部log4j2文件的配置
      LOG_IMPL_FILE=log4j2.xml
      LOGGING_CONFIG=""
      if [ -f "$CONF_DIR/$LOG_IMPL_FILE" ]
      then
          LOGGING_CONFIG="-Dlogging.config=$CONF_DIR/$LOG_IMPL_FILE"
      fi
      CONFIG_FILES=" -Dlogging.path=$LOGS_DIR $LOGGING_CONFIG -Dspring.config.location=$CONF_DIR/ "
      echo -e "Starting the $SERVER_NAME ..."
      nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS $CONFIG_FILES -jar $DEPLOY_DIR/lib/$JAR_NAME > $STDOUT_FILE 2>&1 &
      
      echo "OK!"
      PIDS=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'`
      echo "PID: $PIDS"
      echo "STDOUT: $STDOUT_FILE"
      
    2. stop.sh
      #!/bin/bash
      # 项目名称
      APPLICATION="${project.artifactId}"
      # 项目启动jar包名称
      APPLICATION_JAR="${project.build.finalName}.jar"
      # 通过项目名称查找到PI,然后kill -9 pid
      PID=$(ps -ef | grep "${APPLICATION_JAR}" | grep -v grep | awk '{ print $2 }')
      if [[ -z "$PID" ]]
      then
          echo ${APPLICATION} is already stopped
      else
          echo kill  ${PID}
          kill -9 ${PID}
          echo ${APPLICATION} stopped successfully
      fi
      
  4. 打包项目

    1. 项目下执行mvn clean package命令进行打包,打包后的压缩包路径/project_url/target/test01-1.0-SNAPSHOT-bin.tar.gz;
    2. 打包后的项目文件如下:
      Apache Maven Assembly自定义打包插件的使用
  5. 运行项目

    1. 解压文件:tar -zxvf /project_url/target/test01-1.0-SNAPSHOT-bin.tar.gz
    2. 进入bin目录下:cd /project_url/target/test01-1.0-SNAPSHOT/bin
    3. 运行项目:sh start.sh,运行日志在/project_url/target/test01-1.0-SNAPSHOT/logs路径下
    4. 关闭项目:sh stop.sh