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.rhkb.cn/news/344924.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Burp Suite Professional 2024.5 (macOS, Linux, Windows) - Web 应用安全、测试和扫描

Burp Suite Professional 2024.5 (macOS, Linux, Windows) - Web 应用安全、测试和扫描 Burp Suite Professional, Test, find, and exploit vulnerabilities. 请访问原文链接&#xff1a;Burp Suite Professional 2024.5 (macOS, Linux, Windows) - Web 应用安全、测试和扫描…

将字符串str1复制为字符串str2

定义两个字符数组str1和str2&#xff0c;再设两个指针变量p1和p2&#xff0c;分别指向两个字符数组中的有关字符&#xff0c;通过改变指针变量的值使它们指向字符串中的不同的字符&#xff0c;以实现字符的复制。编写程序&#xff1a; 运行程序&#xff1a; 程序分析&#xff1…

数据库(28)——联合查询

对于union查询&#xff0c;就是把多次查询的结果合并起来&#xff0c;形成一个新的查询结果集。 语法 SELECT 字段列表 FROM 表A... UNION [ALL] SELECT 字段列表 FROM 表B...; 演示 select * from user where age > 22 union all select * from user where age < 50; u…

MyBatisPlus插件生成代码

文章目录 概要安装插件使用插件 概要 MyBatis-Plus 是 MyBatis 的增强工具&#xff0c;旨在简化 MyBatis 的开发。MyBatis-Plus 代码生成器插件可以自动生成项目中常见的代码&#xff0c;如实体类、Mapper 接口、Service 接口和实现类、Controller 等&#xff0c;从而减少手动…

优化家庭网络,路由器无线中继配置全攻略(中兴E1600无线中继设置/如何解决没有预埋有线网络接口的问题/使用闲置路由实现WIFI扩展)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 网络优化 📒📒 操作步骤 📒💡适用场景🚨 常见问题及解决方案⚓️ 相关链接 ⚓️📖 介绍 📖 在现代家庭生活中,WiFi已经渗透到我们生活的每一个角落,成为了日常生活中不可或缺的一部分。然而,不少用户常常遇到W…

JVM学习-监控工具(一)

使用数据说明问题&#xff0c;使用知识分析问题&#xff0c;使用工具处理问题 无监控&#xff0c;不调优&#xff01; 命令行工具 在JDK安装目录下&#xff0c;可以查看到相应的命令行工具&#xff0c;如下图 jps(Java Process Status) 显示指定系统内所有的Hotpot虚拟机…

前端三大件速成 05 javascript(1)js组成、引入、基本语法

文章目录 一、js组成二、js的引入三、基本语法1、变量2、基本规范3、关键字4、数据类型&#xff08;1&#xff09;基本数据类型&#xff08;2&#xff09;引用数据类型&#xff08;3&#xff09;数据类型转换&#xff08;4&#xff09;typeof运算符 5、运算符6、流程控制&#…

轻松构建聊天机器人,大模型 RAG 有了更强大的AI检索器

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

每日两题6

文章目录 删除并获得点数粉刷房子 删除并获得点数 分析 class Solution { public:int deleteAndEarn(vector<int>& nums) {const int N 10001;// 预处理int arr[N] {0};for (int& e : nums)arr[e] e;// 在 arr 上进行 打家劫舍 问题vector<int> f(N),…

《2024年网络安全预测:未来规划深度洞察》

2024 年打击网络对手的计划。 阅读报告&#xff0c;了解我们的专家对 2024 年网络安全行业的预测&#xff0c;包括&#xff1a; 攻击者将人工智能融入其行动中&#xff0c;防御者利用它来加强检测和响应 民族国家继续开展网络行动以实现其地缘政治目标 攻击者继续利用零日漏洞…

Git从入门到放弃

由于我的Git学的不太好&#xff0c;所以为了能够将以后我的学习笔记能够整理的更好&#xff0c;我先要系统的学习一下git&#xff0c;文章由此产生。 文章笔记源自尚硅谷Git入门到精通全套教程视频内容 1 进入官网 学习新技术的第一步需要熟悉官网&#xff0c;Git也不例外。ht…

Java 环境配置 -- Java 语言的安装、配置、编译与运行

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 002 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

为什么要做与运算?网关如何和ip做与运算?

在计算机网络中&#xff0c;“与运算”是一个基本而重要的概念&#xff0c;尤其在IP地址和子网掩码的处理中起着关键作用。本文将解释为什么要进行与运算&#xff0c;以及网关如何和IP地址进行与运算。 为什么要做与运算&#xff1f; 1. 确定网络地址 与运算&#xff08;AND…

自然语言处理:第三十二章HippoRAG:性能提高20% - 受海马体启发的RAG

文章链接: HippoRAG: Neurobiologically Inspired Long-Term Memory for Large Language Models 项目地址: OSU-NLP-Group/HippoRAG: HippoRAG is a novel RAG framework inspired by human long-term memory that enables LLMs to continuously integrate knowledge across e…

二分+模拟,CF1461D - Divide and Summarize

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1461D - Codeforces 二、解题报告 1、思路分析 我们发现每次分裂操作结果都是固定的 我们从初始序列分裂出两个确定的子序列&#xff0c;两个确定的子序列又分裂出4个确定的子序列 那么也就是说…

实验二、网络属性设置《计算机网络》

精神状态 be like&#xff1a;边写边崩溃&#xff0c;越写越得劲儿。 目录 一、实验目的&#xff1a; 二、实验内容 三、实验步骤&#xff1a; 四、实验小结 一、实验目的&#xff1a; 掌握 IP 地址、子网掩码等网络属性的设置。 二、实验内容 预备知识&#xff1a; 1、…

android集成百度文心一言实现对话功能,实战项目讲解,人人都能拥有一款ai应用

大家好&#xff0c;今天给大家讲解下如何实现一个基于百度文心一言的app功能&#xff0c;app内部同时集成了讯飞的语音识别。本文适用于有android基础的小伙伴阅读&#xff0c;文章末尾放上本项目用到的全部实例代码&#xff0c;在使用前请务必看完本文章。 先来给大家看看效果…

php质量工具系列之PHPCPD

PHPCPD 用于检测重复代码&#xff0c;直观的说就是复制粘贴再稍微改改 该工具作者已经 停止维护 安装 composer global require --dev sebastian/phpcpd执行 phpcpd --log-pmd phpcpd_result.xml ./app参数介绍 --log-pmd 将结果保存在phpcpd_result.xml 中 ./app 是phpcpd扫…

编译原理-词法分析(实验 C语言)

编译原理-词法分析 1. 实验目的 设计、编写并调试一个词法分析程序&#xff0c;加深对词法分析原理的理解 2. 实验要求 2.1 待分析的简单语言的词法 关键字&#xff1a;begin&#xff0c;if&#xff0c;then&#xff0c;while&#xff0c;do&#xff0c;end 所有关键字都是…

DevOps入门

DevOps: 让技术团队、运维、测试等团队实现一体式流程自动化 CICD: CI:持续集成 CD:持续交付持续集成:从编码、编译、测试、发布项目到仓库的自动化流程持续交付:包含持续集成&#xff0c;并且增加将项目部署到对应的环境的自动化流程 传统项目闭环流程: DevOps闭环流程…