SpringBoot: 可執行jar的特殊邏輯

這一篇我們來看看Java代碼怎么操作zip文件(jar文件),然后SpringBoot的特殊處理,文章分為2部分

  1. Zip API解釋,看看我們工具箱里有哪些工具能用
  2. SpringBoot的特殊處理,看看SpringBoot Jar和普通Jar的不同

1. Zip API解釋

1. ZipFile

我們先通過ZipFile來讀取jar文件,通過ZipFile#entries()方法返回Zip內的每一個元素,每個元素可能是目錄或文件,如果是目錄則在目標文件夾下創建對應目錄,否則拷貝文件到目標位置

private static void unzipByZipFile(String org, String dest) throws IOException {clean(dest);ZipFile zip = new ZipFile(org);Enumeration<? extends ZipEntry> ez = zip.entries();while (ez.hasMoreElements()) {ZipEntry ze = ez.nextElement();if (ze.isDirectory()) {Files.createDirectories(Path.of(dest, ze.getName()));} else {Path target = Path.of(dest, ze.getName());try (InputStream is = zip.getInputStream(ze)) {Files.copy(is, target);}}}
}

接下來在main方法內調用unzipByZipFile來查看測試效果,并查看輸出的目錄

public static void main(String[] args) throws IOException {unzipByZipFile("D:\\Workspace\\yangsi\\target\\yangsi-0.0.1-SNAPSHOT.jar", "d:/temp");
}
2. ZipInputStream

使用ZipInputStream讀取和ZipFile讀取基本類似,通過getNextEntry先獲取一個ZipEntry,讀取完畢后用closeEntry編譯當前ZipEntry。

private static void unzipByZipInputStream(String org, String dest) throws IOException {clean(dest);try (ZipInputStream zis = new ZipInputStream(new FileInputStream(org))) {ZipEntry ze = null;while ((ze = zis.getNextEntry()) != null) {if (ze.isDirectory()) {Files.createDirectories(Path.of(dest, ze.getName()));} else {Files.copy(zis, Path.of(dest, ze.getName()));}zis.closeEntry();}}
}
3. ZipOuputStream

現在我們使用ZipOutputStream將之前解壓出來的文件重新打包成jar,代碼如下

private static void zipByZipOutputStream(String dir, String dst) throws IOException {Files.deleteIfExists(Path.of(dst));try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dst))) {Path root = Path.of(dir);for (Path x : Files.list(root).toList()) {addToZip(root, x, zos);}}
}private static void addToZip(Path root, Path file, ZipOutputStream zos) throws IOException {if (Files.isDirectory(file)) {for (Path x : Files.list(file).toList()) {addToZip(root, x, zos);}} else {ZipEntry e = new ZipEntry(root.relativize(file).toString());zos.putNextEntry(e);Files.copy(file, zos);zos.closeEntry();}
}

2. SpringBoot的特殊處理

1. 對比文件

到現在為止,一切都看起來很沒好,我們通過ZipInputStream解壓了jar包,然后又通過ZipOutputStream重新打成可執行jar。 直到我們嘗試執行這個通過ZipOutputStream打包的jar,才發現了問題。

~$ java -jar temp.jar
Error: Invalid or corrupt jarfile temp.jar

問題發生在哪呢?處在ZipOutputStream的壓縮級別上,SpringBoot的jar對文件壓縮做了特殊處理。如果我們有3個壓縮文件,分別標號為1、2、3

  1. 文件1,是正常SpringBoot項目通過Maven打包后的結果
  2. 文件2,是將文件1中的jar解壓后,通過ZipOutputStream采用0壓縮級別(不壓縮)打包的文件
  3. 文件3,是將文件1中的jar解壓后,采用默認壓縮級別打包的文件

可以看到org、META-INF在文件1、文件3中的文件大小是完全一致的,所以這部分文件在SpringBoot JAR也是被壓縮的。

而BOOT-INF卻3中方式都不同,我們進入BOOT-INF看看,文件1、文件3中的普通文件(classes、idx)文件是一樣的,也就是普通文件不做壓縮。而文件1、文件2的lib文件夾是一樣的。

所以總結下來,Spring Boot Maven Plugin打成的可執行jar,對普通文件采用了壓縮,而jar文件僅僅打包而不壓縮。這也是為什么我們執行java -jar temp.jar時報錯的原因。

2. 設置jar不壓縮

現在我們要修改ZipOutputStream的輸出,jar文件僅存儲不壓縮,需要在代碼中設置jar的ZipEntry.setMethod(ZipEntry.STORED),同時要自己計算crc和文件大小。

private static void zipByZipOutputStream(String dir, String dst) throws IOException {Files.deleteIfExists(Path.of(dst));try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dst))) {Path root = Path.of(dir);for (Path x : Files.list(root).toList()) {addToZip(root, x, zos);}}
}private static void addToZip(Path root, Path file, ZipOutputStream zos) throws IOException {if (Files.isDirectory(file)) {for (Path x : Files.list(file).toList()) {addToZip(root, x, zos);}} else if (isJar(file)) {ZipEntry e = new ZipEntry(root.relativize(file).toString());long size = Files.size(file);e.setSize(size);e.setCompressedSize(size);e.setMethod(ZipEntry.STORED);try (InputStream fis = Files.newInputStream(file, StandardOpenOption.READ); CheckedInputStream cis = new CheckedInputStream(fis, new CRC32()); ByteArrayOutputStream bos = new ByteArrayOutputStream();) {cis.transferTo(bos);long crc = cis.getChecksum().getValue();e.setCrc(crc & 0xFFFFFFFF);}zos.putNextEntry(e);Files.copy(file, zos);zos.closeEntry();} else {ZipEntry e = new ZipEntry(root.relativize(file).toString());zos.putNextEntry(e);Files.copy(file, zos);zos.closeEntry();}
}private static boolean isJar(Path file) {return file.getFileName().toString().toLowerCase().endsWith(".jar");
}

再次打包后可以看到(文件4),我們打包的文件大小和原始文件是一摸一樣的了。

應該說Spring Boot的這種特殊處理是合理且必要的,jar文件本身已經做過壓縮,再次壓縮意義不大。

現在我們有足夠的背景知識了,下一篇我們來看看SpringBoot可執行Jar是怎么引導并啟動我們的應用的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/24460.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/24460.shtml
英文地址,請注明出處:http://en.pswp.cn/web/24460.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深度學習論文: Emerging Properties in Self-Supervised Vision Transformers

深度學習論文: Emerging Properties in Self-Supervised Vision Transformers Emerging Properties in Self-Supervised Vision Transformers PDF: https://arxiv.org/pdf/2104.14294v1 PyTorch代碼: https://github.com/shanglianlm0525/CvPytorch PyTorch代碼: https://githu…

Composition API函數

unref unref函數用于獲取響應式對象的原始值。如果傳入的是一個響應式對象&#xff0c;則返回其value屬性&#xff1b;如果傳入的是一個普通的JavaScript對象&#xff0c;則返回它本身。 unref函數在某些情況下很有用&#xff0c;例如在使用watchEffect函數時&#xff0c;可…

go語言實戰--基于Vue3+gin框架的實戰Cetide網項目(講解開發過程中的各種踩坑)

最近被要求學習go語言開發&#xff0c;也就做一個項目實戰鞏固一下&#xff0c;也分享一下關于gin框架的實戰項目 &#xff08;后續應該還是會繼續學習Java&#xff0c;這一期還是做一個govue的&#xff09; 經過一段時間的開發過后&#xff0c;感覺現在的開發效率要快不少了&…

傳統工科碩士想轉嵌入式,時間夠嗎?

在開始前剛好我有一些資料&#xff0c;是我根據網友給的問題精心整理了一份「嵌入式的資料從專業入門到高級教程」&#xff0c; 點個關注在評論區回復“888”之后私信回復“888”&#xff0c;全部無償共享給大家&#xff01;&#xff01;&#xff01; 零基礎開始學&#xff0…

[NOVATEK] NT96580行車記錄儀功能學習筆記(持續更新~

一、u-Boot升級燈 運行u-Boot程序時LED燈閃爍,找到運行過程中一直在運行的函數在里面進行LED引腳電平的翻轉 宏定義 Z:\SunFan\AHD580\pip\na51055_PIP\BSP\u-boot\include\configs\nvt-na51055-evb.h Z:\SunFan\AHD580\pip\na51055_PIP\BSP\u-boot\drivers\mtd\nvt_flash_…

什么是JWT(Json-Web-Token)?JWT的用途和優勢是什么?

什么是JWT&#xff08;Json-Web-Token&#xff09;&#xff1f; JWT&#xff0c;全稱JSON Web Token&#xff0c;是一種開放標準&#xff08;RFC 7519&#xff09;。它定義了一種緊湊的、自包含的方式&#xff0c;用于在各方之間安全地傳輸信息。這種信息被設計成JSON對象格式…

簡介RESTful API和中間件Web API網關

從RESTful API開始 RESTful API(Representational State Transfer API)是一種設計Web服務的架構風格,它基于REST(Representational State Transfer)理論。REST是一種軟件架構風格,由Roy Fielding在其博士論文《Architectural Styles and the Design of Network-based So…

LeetCode 550, 380, 234

目錄 550. 游戲玩法分析 IV題目鏈接表要求知識點思路代碼 380. O(1) 時間插入、刪除和獲取隨機元素題目鏈接標簽思路代碼 234. 回文鏈表題目鏈接標簽思路代碼 550. 游戲玩法分析 IV 題目鏈接 550. 游戲玩法分析 IV 表 表Activity的字段為player_id&#xff0c;device_id&am…

Linux卸載殘留MySQL【帶圖文命令巨詳細】

Linux卸載殘留MySQL 1、檢查殘留mysql2、檢查并刪除殘留mysql依賴3、檢查是否自帶mariadb庫 1、檢查殘留mysql 如果殘留mysql組件&#xff0c;使用命令 rpm -e --nodeps 殘留組件名 按順序進行移除操作 #檢查系統是否殘留過mysql rpm -qa | grep mysql2、檢查并刪除殘留mysql…

k8s——Pod容器中的存儲方式及PV、PVC

一、Pod容器中的存儲方式 需要存儲方式前提&#xff1a;容器磁盤上的文件的生命周期是短暫的&#xff0c;這就使得在容器中運行重要應用時會出現一些問題。 首先&#xff0c;當容器崩潰時&#xff0c;kubelet 會重啟它&#xff0c;但是容器中的文件將丟失——容器以干凈的狀態&…

TensorRT 精度debug分析工具

tensorRT還提供了一套可用于engine生成過程中debug的工具&#xff0c;包括Polygraphy、ONNX GraphSurgeon和PyTorch-Quantization。這些小工具用處很大&#xff0c;值得花時間進一步研究。 Debug方法示例 polygraphy Polygraphy是TensorRT官方提供的一系列小工具合集&#x…

spring源碼解析-(1)關于Bean

什么是Bean&#xff1f; 是spring對所有注入到IoC容器中的類的統稱。 我們要注冊進入spirng的bean千奇百怪&#xff0c;所以spring必須需要使用一個統一的定義來標識bean&#xff0c;就有了接下來的BeandDefinition&#xff0c;通過名稱我們就可以知道&#xff0c;他是對bean…

美國演員工會SAG-AFTRA 要求人工智能在廣告中使用演員聲音需征得同意并付費

SAG-AFTRA 的新豁免允許在人工智能生成的廣告中使用演員的聲音&#xff0c;但需要同意、補償和安全措施 美國演員工會&#xff08;SAG-AFTRA&#xff09;推出了一項新的豁免&#xff0c;以保護會員免受未經授權的人工智能在廣告中使用其聲音的影響。動態人工智能音頻廣告豁免定…

C語言Kruskal算法求最小生成樹

Kruskal算法求出最小生成樹。 圖形 算法描述 先找最小權值邊為1的邊有&#xff08;V1&#xff0c;V4&#xff09;&#xff0c;&#xff08;V2&#xff0c;V9&#xff09;&#xff0c;保證不產生回路就可以成功選擇邊 除去上一次找的邊后&#xff0c;在找權值最小的邊為2的有&a…

制作AI問答機器人:從0到1的完整指南

在數字化轉型的浪潮中&#xff0c;企業正追求更高效、智能的客戶服務解決方案。AI問答機器人以其快速響應、全天候服務和持續學習的能力&#xff0c;成為了提升客戶滿意度和加速業務發展的關鍵工具。本文將深入探討如何制作一個企業級的AI問答機器人&#xff0c;并強調其功能體…

OpenAI發表研究論文 介紹了一種逆向工程AI模型工作原理的方法

ChatGPT 開發商 OpenAI 構建人工智能的方法本周遭到了前員工的抨擊&#xff0c;他們指責該公司利用可能有害的技術冒不必要的風險。今天&#xff0c;OpenAI 發布了一篇新的研究論文&#xff0c;目的顯然是為了表明它在通過提高模型的可解釋性來應對人工智能風險方面的認真態度。…

hot100 -- 二分查找

目錄 前言 &#x1f382;搜索插入位置 &#x1f33c;搜索二維矩陣 &#x1f33c;排序數組元素第一和最后一個位置 &#x1f33c;旋轉排序數組 &#x1f4aa;旋轉排序數組中的最小值 &#x1f4aa;兩個正序數組的中位數 前言 二分算法學習_時間超限ac:0%-CSDN博客 &#…

2024年【起重機械指揮】考試及起重機械指揮新版試題

題庫來源&#xff1a;安全生產模擬考試一點通公眾號小程序 起重機械指揮考試考前必練&#xff01;安全生產模擬考試一點通每個月更新起重機械指揮新版試題題目及答案&#xff01;多做幾遍&#xff0c;其實通過起重機械指揮試題及解析很簡單。 1、【多選題】《中華人民共和國特…

【Androi】安卓發展歷程詳解

人不走空 &#x1f308;個人主頁&#xff1a;人不走空 &#x1f496;系列專欄&#xff1a;算法專題 ?詩詞歌賦&#xff1a;斯是陋室&#xff0c;惟吾德馨 目錄 &#x1f308;個人主頁&#xff1a;人不走空 &#x1f496;系列專欄&#xff1a;算法專題 ?詩詞歌…

git推送代碼到github拒絕推送的解決方案

這里描述一下本地推送的場景&#xff0c;首先我在碼云上建立了一個前端項目&#xff0c;進行了自己的個性化開發&#xff0c;后期在github上創建了一個一樣的項目倉庫存放代碼。使用webstorm進行代碼開發。在下面這個位置可以選擇推送的代碼位置。 選擇推送github倉庫之后&…