Java解析嵌套jar中class文件

一、简述

Maven项目通过package打成jar包后,jar包中包含所有依赖lib文件。本文介绍了两种方式解析嵌套jar中的class文件,一种是通过spring-boot-loader包JarFileArchive,另一种是util包中JarFile。

二、JarFileArchive方式

1.spring-boot-loader依赖引入

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
    <version>2.2.4.RELEASE</version>
</dependency>

2.demo案例

java 复制代码
   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 = "file:/"+ URLDecoder.decode(jarPath, StandardCharsets.UTF_8).replaceAll("\\\\","/")+"!/";
        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);
        }
    }

三、JarFile方式

1.demo案例

java 复制代码
   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);
        }
    }

四、两种方式对比

实测项目make-test.jar中所有依赖lib约200个,其中所有class字节码文件约7万多个。方案JarFileArchive约1.5s全部解析,方案JarFile约6s全部解析。

相关推荐
AI你一生一世1 小时前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
java·人工智能·struts·时序数据库·工业智能·dolphindb·ai基础设施
云-缩影1 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
瑞克-桑切斯1 小时前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
海的透彻6 小时前
dockerfile如何快速构建容器(java服务)
java·开发语言
code2cat6 小时前
Java工具篇之Gradle:用任务图组织构建与交付
java·开发语言·单元测试·gradle
后台模板学习7 小时前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
w_zero_one7 小时前
算法--构造
java·数据结构·算法
SL_staff7 小时前
制造业订单交付崩盘?用四层协同框架重建交付可信度(技术实践)
java·产品·全栈
SL_staff8 小时前
打破风控迭代死循环:用决策编排重构业务与技术协作链路
java·产品·全栈
Mikko78 小时前
Redis 连接数满了服务端有错误信息吗?连接数不增加却命令超时?6.2 实测连接池、超时与大 key 怎么判定
java·spring boot·redis·后端