Spring boot默認方式打包因爲打的是全量依賴包(也稱爲fat包),不但打包慢,體積大,傳輸也慢,今天教你們給spring boot瘦瘦身。java
背景
如今微服務架構愈來愈流行,一個項目10多個基於spring boot的服務模塊很常見。假設一個服務模塊打成jar包是100M,那麼一次全量發佈可能就須要上傳1G的文件。在網絡狀況好的時候可能還沒多大感受,但若是是代碼須要拷貝到內網發佈,或者上傳到某些國外服務器上, 將嚴重影響工做效率。web
那麼,有沒有什麼辦法給咱們打的spring boot的jar包瘦瘦身呢?
答案是有,經過相關配置使spring boot打包的時候只加載一些常常會變化的依賴包,好比項目通用的common模塊,一些調用feign接口的API模塊等,而那些固定的不多變化的依賴包則直接上傳到服務器的指定目錄下,在項目啓動的時候經過命令指定lib包加載的目錄就能夠了。這樣,咱們打出來的jar包最多幾M不到,極大的縮小了spring boot項目jar包的體積,提升了發佈上線的效率。算法
補充:
fat jar: 即胖包,打出的jar包包含全部的依賴包。
好處是能夠直接運行,不須要添加其餘命令,壞處是體積太大,傳輸困難。spring
thin jar:即瘦包,打出的jar包只包含一些常常變換的依賴包,通常爲項目中的公共模塊或一些API接口依賴模塊。
好處是體積小,有利於提升項目發佈效率;
壞處是依賴包外置可能存在安全隱患,若是項目的maven依賴變更頻繁,維護服務器上的lib目錄就比較麻煩,也不利於問題定位。tomcat
瘦身運動
一、修改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是引入的公共服務模塊。架構
二、執行maven打包
先執行mvn clean,而後執行mvn package
經過壓解工具查看tax-ws-thin-zip.jar裏面META-INF目錄下的MANIFEST.MF文件:
(至於爲何必定要將Main-Class配置爲PropertiesLauncher 後面再介紹)
三、比較FatJar和ThinJar的體積:
tax-ws-fat.jar是沒有修改配置前打的全量依賴包。
四、從fatJar包中拷貝中lib包到D:\web目錄下
五、經過命令啓動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.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默認擴展類加載器的加載路徑,很容易致使一些問題。最好不要隨便使用。
擴展:雙親委派機制
這裏展開來說就涉及到了java的雙親委派加載機制。
一、BootStrapClassLoader:啓動類加載器。
JAVA_HOME/jre/lib/ext下的類庫(或者經過參數-Djava.ext.dirs指定)。
三、AppClassLoader:應用程序加載器,會加載java環境變量CLASSPATH所指定的路徑下的類庫,而CLASSPATH所指定的路徑能夠經過Systemn.getProperty("java.class.path")獲取,該變量能夠覆蓋。
四、CustomClassLoader:自定義加載器,就是用戶本身定義的CLassLoader,好比tomcat的standardClassLoader屬於這一類。
ClassLoader雙親委派機制:
一、當APPClassLoader加載一個class時,它首先不會本身去加載這個類,而是把類加載請求委派給父類加載器EXTClassloader去完成。
二、當EXTClassLoader加載一個class時,它首先不會去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。
三、若是BottStrapClassLoader加載失敗,會使用EXTClassLoader去嘗試加載。
四、若EXTClassLoader也加載失敗,則會使用APPClassLoader來加載,若是APPClassLoader也加載失敗,則會報出異常ClassNotFundException.
找不到Oracle驅動包的問題
在使用-Djava.ext.dirs=./lib指定外置依賴包的時候,發現加載不到Oracle鏈接的驅動包,這個時候須要添加
-Doracle.jdbc.thinLogonCapability=o3,配置oracle的登陸兼容性
總結
一、爲何要給spring boot工程打的可執行jar包瘦身
二、spring boot的三種啓動器說明
三、如何配置PropertiesLauncher啓動器實現外部依賴包的加載
四、指出了經過指定-Djava.ext.dirs參數實現外部依賴包加載的問題
五、擴展說明了java的雙親委派加載機制
六、外部依賴包加載不到Oracle驅動包的解決辦法
最後
感謝你們最近的支持,雖然說學習是本身的事,可是看見你們的點贊、評論和關注,真的很使人鼓舞,謝謝你們。
我會繼續努力,分享更多優質的技術文章,但願和你們一塊兒交流成長。
更多精彩,關注我吧。
本文分享自微信公衆號 - 跟着老萬學java(douzhe_2019)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。