Spring Boot 可執(zhí)行 jar 可執(zhí)行 war 結(jié)構(gòu)

原文地址 https://docs.spring.io/spring-boot/docs/1.5.19.BUILD-SNAPSHOT/reference/htmlsingle/#executable-jar

Spring引導(dǎo)加載程序模塊允許Spring引導(dǎo)支持可執(zhí)行jar和war文件带污。如果您正在使用Maven或Gradle插件破镰,那么可執(zhí)行jar將自動(dòng)生成,您通常不需要知道它們?nèi)绾喂ぷ鞯募?xì)節(jié)。

如果您需要從不同的構(gòu)建系統(tǒng)創(chuàng)建可執(zhí)行jar,或者您只是對(duì)底層技術(shù)感興趣,那么本節(jié)將提供一些背景知識(shí)。

E.1嵌套的jar

Java沒(méi)有提供任何加載嵌套的jar文件(即包含在jar中的jar文件)的標(biāo)準(zhǔn)方法。如果您希望分發(fā)一個(gè)自包含的應(yīng)用程序稿静,您可以只從命令行運(yùn)行該應(yīng)用程序,而不需要解壓縮辕狰,那么這可能會(huì)有問(wèn)題改备。
為了解決這個(gè)問(wèn)題,許多開(kāi)發(fā)人員使用“shaded”jar蔓倍。shaded jar只是將所有jar中的所有類(lèi)打包到一個(gè)“uber jar”中悬钳。shaded jar的問(wèn)題是,很難看到在應(yīng)用程序中實(shí)際使用的庫(kù)偶翅。如果在多個(gè)jar中使用相同的文件名(但內(nèi)容不同)默勾,也會(huì)出現(xiàn)問(wèn)題。Spring Boot采用了一種不同的方法聚谁,允許您直接嵌套jar母剥。

E.1.1 可執(zhí)行jar文件結(jié)構(gòu)

Spring引導(dǎo)加載程序兼容的jar文件的結(jié)構(gòu)應(yīng)該如下所示:

example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-BOOT-INF
    +-classes
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar

應(yīng)用程序類(lèi)應(yīng)該放在一個(gè)嵌套的BOOT-INF/classes目錄中. 依賴(lài)項(xiàng)應(yīng)該放在一個(gè)嵌套的BOOT-INF/lib目錄中。

E.1.2 可執(zhí)行的war文件結(jié)構(gòu)

兼容Spring Boot Loader的war文件的結(jié)構(gòu)應(yīng)該是這樣的:

example.war
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-WEB-INF
    +-classes
    |  +-com
    |     +-mycompany
    |        +-project
    |           +-YourClasses.class
    +-lib
    |  +-dependency1.jar
    |  +-dependency2.jar
    +-lib-provided
       +-servlet-api.jar
       +-dependency3.jar

依賴(lài)項(xiàng)應(yīng)該放在嵌套的WEB-INF/lib目錄中形导。嵌入式運(yùn)行時(shí)需要但部署到傳統(tǒng)web容器時(shí)不需要的任何依賴(lài)項(xiàng)都應(yīng)該放在WEB-INF/lib-provide中环疼。

E.2 Spring Boot’s “JarFile” class

用于支持加載嵌套jar的核心類(lèi)是org.springframework.boot.loader.jar.JarFile。它允許您加載標(biāo)準(zhǔn)jar文件或嵌套的jar文件朵耕。第一次加載時(shí)炫隶,每個(gè)JarEntry的位置都被映射成外部jar的文件偏移量

myapp.jar
+-------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+----------+|
||     A.class      |||  B.class  |  C.class ||
|+-----------------+||+-----------+----------+|
+-------------------+-------------------------+
 ^                    ^           ^
 0063                 3452        3980

上面的例子展示了A。類(lèi)可以在myapp.jar中的/BOOT-INF/classes中找到相當(dāng)于myapp.jar的0063文件偏移量的位置阎曹。B class 可以在myapp嵌套jar的3452文件偏移量中找到伪阶,C class 可以再3980位置找到。

有了這些信息处嫌,我們可以通過(guò)查找外部jar的適當(dāng)部分來(lái)加載特定的嵌套jar栅贴。我們不需要解壓歸檔文件,也不需要將所有條目數(shù)據(jù)讀入內(nèi)存熏迹。

E.2.1 與標(biāo)準(zhǔn)Java“JarFile”的兼容性

Spring引導(dǎo)加載程序努力保持與現(xiàn)有代碼和庫(kù)的兼容性筹误。org.springframework.boot.loader.jar.JarFilejava.util.jar.JarFile的擴(kuò)展。是可替換的癣缅。getURL()方法將返回一個(gè)URL厨剪,該URL打開(kāi)一個(gè)兼容java.net.JarURLConnection的連接,可以與Java的URLClassLoader一起使用友存。

E.3 啟動(dòng)可執(zhí)行jar

org.springframework.boot.loader.Launcher 啟動(dòng)器類(lèi)是一個(gè)特殊的引導(dǎo)類(lèi)祷膳,它用作一個(gè)可執(zhí)行jar主入口點(diǎn)。它是jar文件中實(shí)際的主類(lèi)屡立,用于設(shè)置適當(dāng)?shù)?code>URLClassLoader并最終調(diào)用main()方法直晨。

有3個(gè)啟動(dòng)器子類(lèi)(JarLauncher, WarLauncherPropertiesLauncher)。它們的目的是加載資源(.class文件等)從目錄中的嵌套jar文件或war文件(與類(lèi)路徑中的顯式文件相反)獲取膨俐。在JarLauncherWarLauncher的情況下勇皇,嵌套路徑是固定的。JarLauncherBOOT-INF/lib/ ,WarLauncherWEB-INF/lib/和WEB-INF/lib-provide/ 中查找焚刺,所以如果您想要更多敛摘,只需在這些位置添加額外的jar即可。默認(rèn)情況下乳愉,PropertiesLauncher會(huì)出現(xiàn)在應(yīng)用程序存檔中的BOOT-INF/lib/中兄淫,您可以通過(guò)設(shè)置環(huán)境變量LOADER_PATHloader來(lái)添加其他位置。

E.3.1 啟動(dòng)程序清單(Launcher manifest)

您需要指定一個(gè)適當(dāng)?shù)膯?dòng)程序作為META-INF/MANIFEST.MF的主類(lèi)屬性蔓姚。應(yīng)該在start類(lèi)屬性中指定要啟動(dòng)的實(shí)際類(lèi)(即您編寫(xiě)的包含main方法的類(lèi))捕虽。

例如,下面是一個(gè)典型的清單坡脐。MF為可執(zhí)行jar文件:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.mycompany.project.MyApplication

對(duì)于war文件泄私,它應(yīng)該是:

Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.mycompany.project.MyApplication

您不需要在清單文件中指定類(lèi)路徑項(xiàng),程序會(huì)自動(dòng)查找路徑备闲。

E.3.2 Exploded archives(歸檔文件)

某些PaaS實(shí)現(xiàn)可能選擇在運(yùn)行前解包存檔晌端。例如,你可以運(yùn)行一個(gè)解壓縮檔案浅役,只需啟動(dòng)適當(dāng)?shù)膯?dòng)器:

$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher

E.4 PropertiesLauncher特性

PropertiesLauncher has a few special features that can be enabled with external properties (System properties, environment variables, manifest entries or loader.properties).

PropertiesLauncher支持從loader.properties以及(由于歷史原因)application.properties加載屬性斩松。我們建議使用loader.properties,作為對(duì)應(yīng)用程序的支持觉既。application.properties已被棄用惧盹,將來(lái)可能會(huì)被刪除。

Key Purpose
loader.path 逗號(hào)分隔的類(lèi)路徑瞪讼,例如lib钧椰、${HOME}/app/lib,類(lèi)似 javac -classpath
loader.home 用于解析loader.path中的相對(duì)路徑。如裝載機(jī)符欠。loader.path=lib ${loader.home}/lib是一個(gè)類(lèi)路徑位置(以及該目錄中的所有jar文件)嫡霞。也用于定位加載loader.properties。示例/opt/app(默認(rèn)為${user.dir})希柿。
loader.args 主方法的默認(rèn)參數(shù)(以空格分隔)
loader.main 要啟動(dòng)的主類(lèi)的名稱(chēng)诊沪,例如com.app.Application
loader.config.name 屬性文件的名稱(chēng)养筒,例如啟動(dòng)器(默認(rèn)為加載器)。
loader.config.location 屬性文件的路徑端姚,例如類(lèi)路徑:loader晕粪。屬性(默認(rèn)為loader.properties)。
loader.system 布爾標(biāo)志渐裸,指示應(yīng)將所有屬性添加到系統(tǒng)屬性中(默認(rèn)為false)

當(dāng)指定為環(huán)境變量或清單項(xiàng)時(shí)巫湘,應(yīng)使用以下名稱(chēng):

Key Manifest entry Environment variable
loader.path Loader-Path LOADER_PATH
loader.home Loader-Home LOADER_HOME
loader.args Loader-Args LOADER_ARGS
loader.main Start-Class LOADER_MAIN
loader.config.location Loader-Config-Location LOADER_CONFIG_LOCATION
loader.system Loader-System LOADER_SYSTEM

構(gòu)建插件在構(gòu)建fat jar時(shí)自動(dòng)將Main-Class屬性移動(dòng)到Start-Class。如果您正在使用它昏鹃,請(qǐng)使用Main-Class屬性指定要啟動(dòng)的類(lèi)的名稱(chēng)尚氛,并省略Start-Class

loader.properties 先在 loader.home查找 然后在classpath根目錄查找, 最后是classpath:/BOOT-INF/classes 使用存在的第一個(gè)位置洞渤。
loader.home 只是一個(gè)附加屬性文件的目錄位置(覆蓋默認(rèn)值)阅嘶,只要load.config存在。沒(méi)有指定位置您宪。
loader.path 可以包含目錄(遞歸掃描jar和zip文件)奈懒、歸檔路徑、歸檔文件中掃描jar文件的目錄(例如宪巨,“dependensis .jar!/lib”)或通配符模式(默認(rèn)JVM行為)磷杏。存檔路徑可以相對(duì)于加載器。home捏卓,或者文件系統(tǒng)中具有jar:file:前綴的任何地方极祸。
loader.path path(如果為空)默認(rèn)為BOOT-INF/lib(意味著從存檔文件運(yùn)行的本地目錄或嵌套目錄)。由于這個(gè)屬性怠晴,當(dāng)沒(méi)有提供額外配置時(shí)遥金,啟動(dòng)器的行為與JarLauncher相同。
loader.path 不能用于配置加載程序的位置蒜田。屬性(用于搜索后者的類(lèi)路徑是啟動(dòng)PropertiesLauncher時(shí)的JVM類(lèi)路徑)稿械。
占位符替換是在使用前從系統(tǒng)和環(huán)境變量以及所有值上的屬性文件本身進(jìn)行的。
屬性的搜索順序是env vars冲粤、系統(tǒng)屬性和loader(在多個(gè)位置查找是有意義的)美莫。屬性,分解的存檔清單梯捕,存檔清單厢呵。

E.5 可執(zhí)行jar的限制

在使用Spring引導(dǎo)加載程序打包的應(yīng)用程序時(shí),需要考慮許多限制傀顾。

E.5.1 Zip實(shí)體壓縮(Zip entry compression)

嵌套實(shí)體 ZipEntry 必須使用 ZipEntry.STORED 方法襟铭,這是必需的,以便我們可以直接查找嵌套jar中的各個(gè)內(nèi)容。嵌套jar文件本身的內(nèi)容仍然可以壓縮寒砖,外部jar中的其他entries也可以壓縮赐劣。

E.5.2 System ClassLoader

啟動(dòng)的應(yīng)用程序在加載類(lèi)時(shí)應(yīng)該使用Thread.getContextClassLoader()(默認(rèn)情況下,大多數(shù)庫(kù)和框架都會(huì)這樣做)入撒。通過(guò)ClassLoader.getSystemClassLoader()加載嵌套的jar類(lèi)將會(huì)失敗隆豹。請(qǐng)注意java.util.Logging總是使用系統(tǒng)類(lèi)加載器,因此您應(yīng)該考慮不同的日志實(shí)現(xiàn)茅逮。

E.6 可選的fat jar 替代方案

如果上述限制意味著您不能使用Spring引導(dǎo)加載程序,可以考慮以下替代方案:

  • Maven Shade Plugin
  • JarClassLoader
  • OneJar
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末判哥,一起剝皮案震驚了整個(gè)濱河市献雅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌塌计,老刑警劉巖挺身,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異锌仅,居然都是意外死亡章钾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)热芹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贱傀,“玉大人,你說(shuō)我怎么就攤上這事伊脓「” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵报腔,是天一觀(guān)的道長(zhǎng)株搔。 經(jīng)常有香客問(wèn)我,道長(zhǎng)纯蛾,這世上最難降的妖魔是什么纤房? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮翻诉,結(jié)果婚禮上炮姨,老公的妹妹穿的比我還像新娘。我一直安慰自己米丘,他們只是感情好剑令,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拄查,像睡著了一般吁津。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天碍脏,我揣著相機(jī)與錄音梭依,去河邊找鬼。 笑死典尾,一個(gè)胖子當(dāng)著我的面吹牛役拴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钾埂,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼河闰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了褥紫?” 一聲冷哼從身側(cè)響起姜性,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎髓考,沒(méi)想到半個(gè)月后部念,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氨菇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年儡炼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片查蓉。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乌询,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奶是,到底是詐尸還是另有隱情楣责,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布聂沙,位于F島的核電站秆麸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏及汉。R本人自食惡果不足惜沮趣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坷随。 院中可真熱鬧房铭,春花似錦、人聲如沸温眉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)类溢。三九已至凌蔬,卻和暖如春露懒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背砂心。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工懈词, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辩诞。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓坎弯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親译暂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抠忘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容