給你的SpringBoot工程打的jar包瘦瘦身

Spring boot默認方式打包由於打的是全量依賴包(也稱爲fat包),不但打包慢,體積大,傳輸也慢,今天教大家給spring boot瘦瘦身。

背景

現在微服務架構越來越流行,一個項目10多個基於spring boot的服務模塊很常見。假設一個服務模塊打成jar包是100M,那麼一次全量發佈可能就需要上傳1G的文件。在網絡情況好的時候可能還沒多大感覺,但如果是代碼需要拷貝到內網發佈,或者上傳到某些國外服務器上, 將嚴重影響工作效率。

那麼,有沒有什麼辦法給我們打的spring boot的jar包瘦瘦身呢?
答案是有,通過相關配置使spring boot打包的時候只加載一些經常會變化的依賴包,比如項目通用的common模塊,一些調用feign接口的API模塊,而那些固定的依賴包則直接上傳到服務器的指定目錄下,在項目啓動的時候通過命令指定lib包加載的目錄就可以了。這樣,我們打出來的jar包最多幾M不到,極大的縮小了spring boot項目jar包的體積,提高了發佈上線的效率。

補充:
fat jar: 即胖jar,打出的jar包包含所有的依賴包。
好處是可以直接運行,不需要添加其他命令,壞處是體積太大,傳輸困難。

**thin jar:**即瘦包,打出的jar包只包含一些經常變換的依賴包,一般爲項目中的公共模塊或一些API接口依賴模塊。
好處是體積小,有利於提高項目發佈效率;
壞處是依賴包外置可能存在安全遺患,如果項目的maven依賴變動頻繁,維護服務器上的lib目錄就比較麻煩,也不利於問題定位。

瘦身運動

1、修改maven打包參數

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <layout>ZIP</layout>
                     <includes>
                         <include>
                             <groupId>nothing</groupId>
                             <artifactId>nothing</artifactId>
                         </include>
                         <include>
                             <groupId>com.huacloud.tax.rpc</groupId>
                             <artifactId>common</artifactId>
                         </include>
                     </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

說明:

layout
用來配置可執行jar包中Main-Class的類型,這裏一定要設置爲 ZIP,使打的jar包中的Main-Class爲PropertiesLauncher 。

includes
將需要保留的jar包,按照groupId和artifactId(注意兩個都是必填項)include進來。
nothing 代表不存在的依賴包,意思就是什麼依賴包都不引入
common是引入的公共服務模塊。

2、執行maven打包
先執行mvn clean,然後執行mvn package
在這裏插入圖片描述
將target目錄下打好的包複製到D:\web目錄下,重命名爲tax-ws-thin-zip.jar。

通過壓解工具查看tax-ws-thin-zip.jar裏面META-INF目錄下的MANIFEST.MF文件:
在這裏插入圖片描述
發現Main-Class的值確實變爲了PropertiesLauncher ,說明我們的配置成功。
(至於爲什麼一定要將Main-Class配置爲PropertiesLauncher 後面再介紹)

3、比較FatJar和ThinJar的體積:
在這裏插入圖片描述
可以發現,tax-ws-thin.jar這個瘦包的體積比胖包的體積小了非常多。

4、從fatJar包中拷貝中lib包到D:\web目錄下
在這裏插入圖片描述
5、通過命令啓動jar包

D:\web>java -Dloader.path="D:\web\lib"  -jar tax-ws-thin.jar

通過啓動參數loader.path配置外置依賴包的加載路徑。

項目成功啓動,說明我們配置的外包依賴包加載生效。

原理探究

爲什麼將可執行jar包的Main-Class設置爲PropertiesLauncher就可以通過配置啓動參數loader.path指定依賴包的加載路徑呢?
首先我們對spring boot可執行jar包實現原理中的啓動器Launcher有所瞭解。

以下摘自spring boot官網:
org.springframework.boot.loader.Launcher類是特殊的引導程序類,用作可執行jar的主要入口點。它是jar文件中的實際Main-Class,用於設置適當的URLClassLoader並最終調用main()方法。

有三個啓動器子類(JarLauncher,WarLauncher和PropertiesLauncher)。它們的目的是從目錄中的嵌套jar文件或war文件(而不是在類路徑中顯式的文件)加載資源(.class文件等)。對於JarLauncher和WarLauncher,嵌套路徑是固定的。 JarLauncher位於BOOT-INF / lib /中,而WarLauncher位於WEB-INF / lib /和WEB-INF / lib-provided /中。如果需要,可以在這些位置添加額外的罐子。默認情況下,PropertiesLauncher在您的應用程序存檔中的BOOT-INF / lib /中查找。您可以通過在loader.properties(這是目錄,歸檔文件或歸檔文件中的目錄的逗號分隔列表)中設置一個稱爲LOADER_PATH或loader.path的環境變量來添加其他位置。
————————————————

也就是說啓動器Launcher是爲了項目啓動加載依賴資源的,共有3個啓動器(JarLauncher,WarLauncher和PropertiesLauncher),其中JarLauncher和WarLauncher加載資源的路徑是固定的,而PropertiesLauncher可以通過環境變量loader.path來指定加載資源的位置。
在這裏插入圖片描述

layout屬性值說明:

JAR,即通常的可執行jar
Main-Class: org.springframework.boot.loader.JarLauncher

WAR,即通常的可執行war,需要的servlet容器依賴位於
Main-Class: org.springframework.boot.loader.warLauncher

ZIP,即DIR,類似於JAR
Main-Class: org.springframework.boot.loader.PropertiesLauncher
(記住這個就好,其他的應用場景比較少)

PropertiesLauncher屬性配置

PropertiesLauncher具有一些可以通過外部屬性(系統屬性,環境變量,清單條目或loader.properties)啓用的特殊功能。 下表描述了這些屬性:

Key 目的
loader.path lib包加載路徑
loader.home 用於解析loader.path中的相對路徑。 例如,給定loader.path = lib,則$ {loader.home} / lib是類路徑位置(以及該目錄中的所有jar文件)。 此屬性還用於查找loader.properties文件,如以下示例/ opt / app所示。它默認爲$ {user.dir}。
loader.args main方法的默認參數(以空格分隔)。
loader.main 要啓動的主類的名稱(例如com.app.Application)
loader.config.name 屬性文件的路徑(例如,classpath:loader.properties)。 默認爲loader.properties。
loader.system 布爾值標誌,指示應將所有屬性添加到系統屬性。 默認爲false。

更過資料可以查看官網的關於spring boot可執行jar包的說明文檔:The Executable Jar Format

陷阱糾正

之前在網上看到過一種沒有配置layout=ZIP的方式,而是直接打成瘦包後,在啓動命令中通過-Djava.ext.dirs來指定外置依賴包的加載路徑。

D:\web>java -Djava.ext.dirs="D:\web\lib"  -jar tax-ws-thin.jar

原理解析:
-Djava.ext.dirs會覆蓋Java本身的ext設置,java.ext.dirs指定的目錄由ExtClassLoader加載器加載,如果您的程序沒有指定該系統屬性,那麼該加載器默認加載$JAVA_HOME/jre/lib/ext目錄下的所有jar文件。但如果你手動指定系統屬性且忘了把$JAVA_HOME/jre/lib/ext路徑給加上,那麼ExtClassLoader不會去加載$JAVA_HOME/lib/ext下面的jar文件,這意味着你將失去一些功能,例如java自帶的加解密算法實現。

所以,通過這種寫法,直接強行修改java默認擴展類加載器的加載路徑,很容易導致一些問題。最好不要隨便使用。

找不到Oracle驅動包的問題

在使用-Djava.ext.dirs配置外置依賴包加載路徑的時候,出現了加載不到Oracle的驅動包的問題,這個時候需要添加
-Doracle.jdbc.thinLogonCapability=o3,配置oracle的登錄兼容性

擴展:雙親委派機制

這裏展開來講就涉及到了java的雙親委派加載機制。
在這裏插入圖片描述

1、BootStrapClassLoader:啓動類加載器,該ClassLoader是在啓動時候創建的,是寫在JVM內核裏的,它不是一個字節碼文件,是由c++編寫的二進制代碼,所以開發者無法獲取到該啓動類的引用,也就不能通過引用來進行操作。這個加載器是加載$JAVA_HOME/jre/lib下面的類庫(或者通過參數-Xbootclasspath指定)。

2、EXTClassLoader:擴展類加載器,ExtClassLoader會加載 $JAVA_HOME/jre/lib/ext下的類庫(或者通過參數-Djava.ext.dirs指定)。

3、AppClassLoader:應用程序加載器,會加載java環境變量CLASSPATH所指定的路徑下的類庫,而CLASSPATH所指定的路徑可以通過Systemn.getProperty(“java.class.path”)獲取,該變量可以覆蓋。

4、CustomClassLoader:自定義加載器,就是用戶自己定義的CLassLoader,比如tomcat的standardClassLoader屬於這一類。

ClassLoader雙親委派機制:
1、當APPClassLoader加載一個class時,它首先不會自己去加載這個類,而是把類加載請求委派給父類加載器EXTClassloader去完成。

2、當EXTClassLoader加載一個class時,它首先不會去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。

3、如果BottStrapClassLoader加載失敗,會使用EXTClassLoader去嘗試加載。

4、若EXTClassLoader也加載失敗,則會使用APPClassLoader來加載,如果APPClassLoader也加載失敗,則會報出異常ClassNotFundException.

總結

1、爲什麼要給spring boot工程打的可執行jar包瘦身
2、spring boot的三種啓動器說明
3、如何配置PropertiesLauncher啓動器實現外部依賴包的加載
4、指出了通過指定-Djava.ext.dirs參數實現外部依賴包加載的問題
5、擴展說明了java的雙親委派加載機制
6、外部依賴包加載不到Oracle驅動包的解決辦法

最後

感謝大家最近的支持,雖然說學習是自己的事,但是看見大家的點贊、評論和關注,真的很令人鼓舞,謝謝大家。
我會繼續努力,分享更多優質的技術文章,希望和大家一起交流成長。

更多精彩,關注我吧。
圖注:跟着老萬學java

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章