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.mzph.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…

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_…

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仓库之后&…

Python深度学习基于Tensorflow(16)基于Tensorflow的对话实例

文章目录 基础数据清洗数据生成词汇表定义分词器并制作数据集构建Transformer模型并训练模型推理 Tensorflow 的核心就是注意力机制&#xff0c;在之前详细的介绍过&#xff0c;具体可以看这个&#xff1a;Python深度学习基于Tensorflow&#xff08;9&#xff09;注意力机制_te…

什么情况下需要配戴助听器

以下几种情况需要考虑配戴助听器&#xff1a; 1、听力无波动3个月以上的感音神经性听力障碍。如:先天性听力障碍、老年性听力障碍、噪声性听力障碍、突聋的稳定期等&#xff0c;均可选配合适的助听器。 2、年龄方面。使用助听器没有严格的年龄限制&#xff0c;从出生数周的婴…

深度学习Week16——数据增强

文章目录 深度学习Week16——数据增强 一、前言 二、我的环境 三、前期工作 1、配置环境 2、导入数据 2.1 加载数据 2.2 配置数据集 2.3 数据可视化 四、数据增强 五、增强方式 1、将其嵌入model中 2、在Dataset数据集中进行数据增强 六、训练模型 七、自定义增强函数 一、前言…

Geoserver源码解读一(环境搭建)

一、Github地址 https://github.com/geoserver/geoserver 1.1 克隆代码 git clone https://github.com/geoserver/geoserver.git 1.2 选择版本 版本选择参考我的上一篇文章 Geoserver 以及 Geotools各版本和jdk版本对照表 此处我选择的是兼容jdk8的最后一个版本 git che…