SpringBoot專案打包蘿蔔與坑
一、SpringBoot專案,打成預設的Jar包
應用背景:
該場景較為常見,沒有特殊需求時,都可以採用這種打包方式
操作步驟:
SpringBoot專案打成預設的Jar包是最簡單的一種打包方式,預設情況下,我們幾乎不需要增加任何額外的配置,只要我們執行相關的打包命令即可(maven)mvn clean package
就可以完成相應的打包操作;這時,maven會幫助我們將我們的程式碼和依賴庫打成一個Jar包,包名在pom.xml檔案中進行配置。打包完成後,我們之間執行java -jar <package.jar>
即可。
補充:SpringBoot打包後的目錄格式如下:
看著東西挺多了,其實我們只要關注2個點即可:第一是目錄BOOT-INF,第二是META-INF
二、SpringBoot專案,將原始碼與依賴庫分離打包
應用背景:
在某些時候,我們可能會有一些需求,比如將專案的原始碼與依賴庫和配置分別打包,期望各自相互獨立。
真實案例:
58同城自主研發了RPC框架"SCF",我們使用SpringBoot呼叫SCF相關的服務,然後做相關處理和業務邏輯,這都不是重點;重點是SCF在啟動時要對所有其依賴的Jar包進行掃描,如果沒能掃描到,那就SCF的類載入器將無法載入這些Jar包,並且最重要的來了, SCF的掃描是基於檔案系統路徑的 ,也就是說,此時,如果我們按照第一種方式打包的話,SCF在去掃描其依賴的Jar包時,真實的Jar包是在我們打好的工程Jar包裡面——我習慣把它稱之為“jar中jar”,這時候按照檔案系統路徑去掃描的時候,是無法載入到這些Jar的。最直觀的結果就是我們的工程打包之後無法執行。那麼此時我們要如何解決這個問題呢?
問題定位:遇到實現方式為基於檔案系統掃描時,遇到了jar中Jar這種情況,就將無法正確載入相關的類
問題分析:避免Jar中Jar,我們需要避免Jar中Jar找不到的情況
解決方案:1.打包時分離原始碼與類庫及配置,保證載入的東西都是在檔案系統路徑下可尋找的。2.如果是Web專案考慮打成war包,war包在部署到web容器中後,會自動解壓,也就變成了單層Jar,也能避免Jar中Jar
如何分離打包呢?
一、首先引入Maven的Assembly外掛maven-assembly-plugin
,配置細節如下:
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <appendAssemblyId>false</appendAssemblyId> <descriptors> <descriptor>src/main/resources/assembly.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin>
然後建立一個assembly.xml配置檔案,增加我們打包的細節配置的描述:
1.我們把最終的工程打成一個ZIP包
2.這個ZIP包中包含了幾個元素: 打包格式,我們選擇的是ZIP,ZIP裡包含的是我們的配置檔案,我們工程的配置檔案一般放在src/main/resources中,所以這裡我們把這個資料夾下的全部配置都打加進來,輸出的包名為config,bin,lib同理,lib中包含了全部的依賴Jar
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>package</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>*.*</include>
</includes>
<filtered>true</filtered>
<outputDirectory>${file.separator}config</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/resources/runScript</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.directory}/lib</directory>
<outputDirectory>${file.separator}lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>${file.separator}</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>
二、引入maven-jar-plugin
外掛;這裡要指定啟動類,<useUniqueVersions>false</useUniqueVersions>
這個配置是一個非常關鍵的配置,此處存在著一個大深坑,如果你沒遇到過的話…請參考,如果不配置此項,預設值為true,在快照發布後,我們download到本地倉庫的Jar包會出現2個,一個是帶時間戳的,一個不帶,我們引用的是不帶時間戳的,但是下載的時候會下載成帶時間戳的,這是因為在打包的時候,META-INF中的classpath配置裡,引用的Jar包的名稱是帶時間戳的,這樣就會導致最終找不到類。參考下圖所示:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.package.SpringbootPassportApplication</mainClass>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
</archive>
</configuration>
</plugin>
三、引入依賴管理外掛maven-dependency-plugin
,這個外掛主要作用是分類依賴庫,簡單是說就是把我們依賴的Jar包(lib)打在工程外面,路徑就是在${project.build.directory}/lib
中,注意:includeScope,如果配置為compile,那麼專案依賴的執行時依賴是不會被打包的,在執行時可能會出現類找不到或者其它的異常,所以想要避免這個問題,需要把includeScope配置為runtime。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-lib</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime
</includeScope>
</configuration>
</execution>
</executions>
</plugin>
四、結果
執行打包命令後,ZIP包內容如下所示:,我們解壓後,執行java -jar <project.jar>
,專案成功執行,且無異常
五、補充(之前踩過的坑)
由於專案本質是一個web專案,此處只是打了Jar包,所以一定要注意的是,在引入專案依賴的時候,不可以排除spring-boot-starter-web
中對內建tomcat的依賴,也不可以將spring-boot-starter-tomcat
中的scope設定為provided,因為我們打Jar包執行時需要依賴相關的web容器。——有人會問為什麼要排除這個依賴,是因為在嘗試打war包的時候,對這些配置進行排除,然後切換成Jar後,發現專案執行不起來,排查了很久,浪費了不少時間,也算是自己給自己挖的坑,希望大家在切換的時候避免這個問題。
文章總結:
0.SpringBoot單獨打包無法載入Jar包中的配置和Jar
1.SpringBoot分離打包的方法
2.SpringBoot分離打包需要的Maven依賴和配置檔案
3.SpringBoot引入快照版本的依賴打包時,META-INF中尋找的classpath值帶時間