Apache Maven Assembly自定義打包外掛的使用

2023-02-28 15:01:21

前言

  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. 打包後的專案檔案如下:
  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