利用spring-boot-thin-launcher插件分離jar包的依賴和配置

Spring Boot項目可以通過spring-boot-maven-plugin插件打包生成一個可執行的jar包,這樣可以脫離web容器(例如tomcat)直接運行。但默認情況下spring-boot-maven-plugin打出來的包是一個fat jar,即將所有的依賴全部打進了jar包當中,這樣的jar包體積很大,每次更新系統的時候都需要完整替換整個jar包(本地還好,如果是雲服務器,網速慢了每次上傳文件都想砸電腦π__π)。此外,系統切換環境時,也同時需要切換配置參數,雖然可以使用配置中心或者利用命令行參數修改配置,但有時候也免不了直接需要修改配置文件,這樣的話就有必要將配置文件從jar包中分離出來,單獨存放。

Spring社區大概也考慮到了部分開發者有這樣的需求,所以提供了spring-boot-thin-launcher這個插件用來將項目的依賴和配置從jar包中分離出去。這個插件雖然是放到spring-projects-experimental(意思就是實驗性質的項目)當中的,但從我使用的經驗來看應該是比較穩定的,能夠滿足絕大部分場景的需求。廢話少說,還是先上代碼吧:

<build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <!--項目的執行入口-->
                    <mainClass>com.example.Application</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.boot.experimental</groupId>
                        <artifactId>spring-boot-thin-layout</artifactId>
                        <version>1.0.12.RELEASE</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot.experimental</groupId>
                <artifactId>spring-boot-thin-maven-plugin</artifactId>
                <version>1.0.12.RELEASE</version>
                <executions>
                    <execution>
                        <!--在編譯時下載依賴包 -->
                        <id>resolve</id>
                        <goals>
                            <goal>resolve</goal>
                        </goals>
                        <inherited>false</inherited>
                    </execution>
                </executions>
            </plugin>
            <!--移動配置文件到外部文件夾-->
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <tasks>
                                <move file="${project.build.directory}/classes/application.yml" todir="${project.build.directory}/thin/root/config"/>
                                <copy todir="${project.build.directory}/thin/root/">
                                    <fileset dir="${basedir}/bin"/>
                                </copy>
                            </tasks>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

在maven-antrun-plugin中可以根據需要定義一些額外的任務,比如移動其它的配置文件,或者將外部的一些文件加入到項目目錄中(比如執行腳本等)。
這樣配置後,打包以後的target目錄大概是這樣的:



其中thin目錄下面就可以作爲整個項目的部署目錄了,config是配置文件的存放目錄,respository下面是所有的依賴包,backend-2.0.jar中僅包含了項目自身的資源,體積比之前小太多了。

運行原理

那爲什麼執行時,系統知道自動去外部加載依賴和配置呢?這個問題需要首先了解打包後的結構,把backend-2.0.jar解壓之後,會發現除了我們自己的類和資源以外,還多了一個類:
org.springframework.boot.loader.wrapper.ThinJarWrapper,其實我們在運行項目時,這個類纔是真正的項目入口,它會在默認位置查找項目相關的依賴,如果沒有找到,甚至還會從指定的maven倉庫中直接下載,所以啓動時系統能夠識別到外部的依賴包。至於外部配置,是因爲Spring Boot框架在讀取配置文件時,會默認讀取幾個目錄下的配置文件,其中優先級最高的就是當前目錄下的config目錄(所以config目錄的名字不能改成其它的)。

執行jar包的時候需要注意,要額外添加一個參數來指定依賴包所在的倉庫位置(在我們的配置中就在jar包的當前文件夾),例如:-Dthin.root=. 默認的位置是${user.home}/.m2,如果倉庫中沒有需要的依賴,啓動jar包時還會自動連接遠程倉庫進行下載,導致啓動時間非常長,這一點需要注意。spring-boot-thin-launcher還有很多可配置的參數,具體可以到 官網 上自行查看。

另外附上一個通用的spring-boot-thin-launcher打包文件的啓動腳本:

#!/bin/bash
#這裏指定需要運行的jar包的名字
APP_NAME="your-jar-name.jar"
JVM_ARGS="-Xms512M -Xmx2048M -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/log/heap.hprof"
THIN_ARGS="-Dthin.root=. -Dthin.offline=true"
#使用說明,用來提示輸入參數
usage() {
    echo "Usage: sh 執行腳本.sh [start|stop|restart|status|run]"
    exit 1
}
 
#檢查程序是否在運行
is_exist(){
  pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}' `
  #如果不存在返回1,存在返回0
  if [ -z "${pid}" ]; then
   return 1
  else
    return 0
  fi
}
 
#後臺啓動
start(){
  is_exist
  if [ $? -eq "0" ]; then
    echo "${APP_NAME} is already running. pid=${pid} ."
  else
    echo "${APP_NAME} running with args: nohup java $THIN_ARGS -jar $JVM_ARGS $APP_NAME "
    nohup java $THIN_ARGS -jar $JVM_ARGS $APP_NAME >> catalina.out 2>&1 &
  fi
}
#前臺啓動
run(){
  is_exist
  if [ $? -eq "0" ]; then
    echo "${APP_NAME} is already running. pid=${pid} ."
  else
    echo "${APP_NAME} running with args: java $THIN_ARGS -jar $JVM_ARGS $APP_NAME"
    java $THIN_ARGS -jar $JVM_ARGS $APP_NAME 
  fi
}
 
#停止方法
stop(){
  is_exist
  if [ $? -eq "0" ]; then
    kill -9 $pid
  else
    echo "${APP_NAME} is not running"
  fi
}
 
#輸出運行狀態
status(){
  is_exist
  if [ $? -eq "0" ]; then
    echo "${APP_NAME} is running. Pid is ${pid}"
  else
    echo "${APP_NAME} is NOT running."
  fi
}
 
#重啓
restart(){
  stop
  start
}
 
#根據輸入參數,選擇執行對應方法,不輸入則執行使用說明
case "$1" in
  "start")
    start
    ;;
  "run")
    run
    ;;
  "stop")
    stop
    ;;
  "status")
    status
    ;;
  "restart")
    restart
    ;;
  *)
    usage
    ;;
esac
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章