Java解析嵌套jar中class文件过程
一、简述
用Ma ven构建项目,最终打包成jar文件时,一个常见的结果是:这个jar包里,不仅包含了你自己的代码,还打包了所有依赖的库文件。这就形成了一个“嵌套”的结构。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

那么问题来了,如果想直接从这个“大包”里,解析出嵌套在内部那些依赖jar包中的class文件,该怎么办?别急,下面就来聊聊两种主流且实用的方法:一种是借助Spring Boot生态里的spring-boot-loader包提供的JarFileArchive;另一种则是使用Ja va标准库里的ja va.util.jar.JarFile。两种方式各有特点,咱们逐一拆解。
二、JarFileArchive方式
1. spring-boot-loader依赖引入
要使用第一种方法,首先得把工具包引入项目。在Ma ven的pom.xml里加上下面这段依赖即可:
org.springframework.boot spring-boot-loader 2.2.4.RELEASE
注意,版本号可以根据实际情况调整。
2. demo案例
这里有个细节需要提醒:不同版本的spring-boot-loader,其getNestedArchives方法签名可能不同。比如在较早版本中用的方法,在高版本里可能已被标记为废弃,转而使用参数更丰富的新方法。所以,实际编码时最好跟进一下你所使用版本的官方源码,做相应调整。
核心逻辑其实很清晰,看下面的代码示例就明白了:
public static void main(String args[]) throws Exception {
String jarPath = "C:\\Users\\root\\Desktop\\make-test.jar";
// 方案一:spring-boot-loader
long start1 = System.currentTimeMillis();
getClassInfoByJarLib(jarPath);
long end1 = System.currentTimeMillis();
log.info("收集所有lib类ClassInfo,花费时间={}",(end1-start1));
}
public static void getClassInfoByJarLib(String jarPath) {
String filePath;
String separator = File.separator;
if("\\".equals(separator)){
// windows系统使用如下路径
filePath = "file:/"+ URLDecoder.decode(jarPath, StandardCharsets.UTF_8).replaceAll("\\\\","/")+"!/";
}else if("/".equals(separator)){
// linux系统使用如下路径
filePath = "file:"+ URLDecoder.decode(jarPath, StandardCharsets.UTF_8).replaceAll("\\\\","/")+"!/";
}else {
throw new RuntimeException("未知操作系统,解析jarLib失败");
}
String rootJarPath = "jar:"+ filePath;
try {
JarFileArchive jarFileArchive = new JarFileArchive(new Handler().getRootJarFileFromUrl(new URL(rootJarPath)));
// getNestedArchives获取嵌套的jar等文件,参数是个EntryFilter,用于过滤
jarFileArchive.getNestedArchives(entry -> entry.getName().startsWith("BOOT-INF/lib/") && entry.getName().endsWith(".jar"))
.forEach(archive -> {
archive.iterator().forEachRemaining(entry -> {
String entryName = entry.getName();
// 过滤出嵌套jar包中的字节码文件
if (entryName.endsWith(".class")) {
String className = entryName.replace('/', '.').replace(".class", "");
log.info("className:{}",className);
}
});
});
} catch (IOException e) {
log.error("解析嵌套jarLib中ClassInfo异常,jarPath={}",jarPath,e);
throw new RuntimeException(e);
}
}
这段代码的关键在于,它利用JarFileArchive这个封装好的工具,能够非常方便地遍历出嵌套在BOOT-INF/lib/目录下的所有jar包,并进一步解析出里面的每一个class文件。路径处理和异常捕获也都考虑进去了,开箱即用。
三、JarFile方式
1. demo案例
如果你不想引入额外的依赖,纯用JDK自带的能力行不行?当然可以。Ja va标准库中的JarFile类同样能完成这个任务,只不过需要自己多写几行处理逻辑。
public static void main(String args[]) throws Exception {
String jarPath = "C:\\Users\\root\\Desktop\\make-test.jar";
// 方案二:JarFile
long start2 = System.currentTimeMillis();
processJar(jarPath);
long end2 = System.currentTimeMillis();
log.info("收集所有lib类ClassInfo,花费时间={}",(end2-start2));
}
private static void processJar(String jarPath){
try (JarFile jarFile = new JarFile(new File(jarPath))) {
jarFile.stream().parallel()
// 过滤出所有符合要求的jar包
.filter(entry -> !entry.isDirectory() && entry.getName().startsWith("BOOT-INF/lib/") && entry.getName().endsWith(".jar"))
.forEach(entry -> processNestedJar(jarFile, entry.getName()));
} catch (IOException e) {
log.error("解析嵌套jarLib中ClassInfo异常,jarPath={}",jarPath,e);
throw new RuntimeException(e);
}
}
private static void processNestedJar(JarFile jarFile, String entryName){
// 处理嵌套jar文件
try (InputStream nestedJarStream = jarFile.getInputStream(jarFile.getJarEntry(entryName));
JarInputStream jarInputStream = new JarInputStream(nestedJarStream)) {
JarEntry nestedEntry;
while ((nestedEntry = jarInputStream.getNextJarEntry()) != null) {
if (nestedEntry.isDirectory()) {
continue;
}
String nestedEntryName = nestedEntry.getName();
if (!nestedEntryName.endsWith(".class")) {
continue;
}
try {
String className = nestedEntryName.replace('/', '.').replace(".class", "");
log.info("className:{}",className);
} catch (Exception e) {
log.error("目标类={}查找失败",nestedEntryName,e);
throw new RuntimeException(e);
}
}
} catch (IOException e) {
log.error("目标类={}查找失败",entryName,e);
throw new RuntimeException(e);
}
}
这种方式的核心是使用JarFile和JarInputStream进行两层遍历。第一层找到嵌套的jar包,第二层再像解压缩一样,逐个读取嵌套jar里的class条目。代码量稍多,但胜在零依赖,理解起来也更底层。
四、两种方式对比
光说不练假把式,性能如何还得看实测。我们用一个名为make-test.jar的实际项目进行测试,其中大约包含了200个依赖lib,总计超过7万个class字节码文件。
结果很有意思:
- 使用
JarFileArchive方案,解析全部文件大约耗时 1.5秒。 - 使用标准
JarFile方案,完成同样的工作则花了大约 6秒。
差距显而易见。这主要是因为spring-boot-loader中的JarFileArchive为这种嵌套场景做了深度优化,封装了更高效的遍历逻辑。而纯JDK方案虽然通用,但在处理大量嵌套条目时,性能开销相对较大。
总结
简单总结一下:如果你的项目已经是Spring Boot体系,或者不介意引入一个轻量级的工具包,那么JarFileArchive无疑是更高效、更优雅的选择。如果你追求极致的“纯净”,或者只是在写一个简单的工具,那么基于标准JarFile的方案也能可靠地完成任务,只是需要多付出一点时间成本。
两种方法的核心思路和代码实现都在上面了,可以根据自己的实际场景灵活选用。希望这份梳理能为大家在处理类似问题时,提供一个清晰的参考。
您可能感兴趣的文章:
- ja va开发读取嵌套jar包中的文件
- windows后台运行Ja va jar包实现方式
- Ja va中-jar命令参数设置的完整指南
- Ja va JAR包运行与反编译实践
- ja va -jar启动原理详解(附实操验证和注意事项)
- mysql-connector-ja va.jar包的下载实践
热门专题
热门推荐
在数字货币快速发展的今天,如何选择一个靠谱的交易平台,往往是新手投资者迈出的第一步。面对市场上琳琅满目的交易所APP,从安全性、易用性到功能特色,究竟该怎么选?下面,我们就来梳理一下2026年主流的数字资产交易平台,帮你从多个维度看清它们的核心特点,无论是想尝试简单的现货买卖,还是计划涉足合约交易,
从音乐人到AI药物研发创业者:Aloe Blacc的跨界创业之路 近日,美国知名创作歌手Aloe Blacc做客TechCrunch旗下知名播客Equity,分享了他从音乐界成功跨界至AI驱动抗癌药物研发领域的独特经历。尤为引人关注的是,他创立的AI医药公司至今未进行任何外部融资。在访谈中,他深入阐
AI文生视频:从“猎奇玩具”到“生产力工具”的疾速进化 还记得几年前全网疯传的“威尔·史密斯吃意大利面”吗?那段画面扭曲、动作诡异的视频,一度成为AI文生视频技术稚嫩期的经典注脚——与其说是创作,不如说是一场数字世界的“恐怖谷”体验,离实际应用相距甚远。 然而,技术的演进速度总是超乎想象。过去一年,
百度开源文生图模型ERNIE-Image:消费级显卡畅享顶级文字生成效果 2024年4月15日,百度文心大模型团队正式宣布开源其参数规模达80亿的文生图模型ERNIE-Image。该模型最引人注目的优势在于,仅需24GB显存的消费级GPU即可实现高效部署与运行。同时,团队还发布了推理加速版本ERNI
欧亿交易所现货交易时间:如何理解其全球化设计逻辑? 在数字资产交易的世界里,交易时间的设定绝非小事。它直接关系到投资者的操作空间能否打开,以及整个市场的流动性是否充沛。作为行业内的头部平台,欧亿交易所(OYEX)在现货交易时间上的安排,可以说是一份深思熟虑的“全球时区解决方案”。它的设计,精准地瞄准





