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全部解析。

相关推荐
微尘寒风6 小时前
【Git】的安装和使用
java·git
y = xⁿ7 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
侧耳倾听1117 小时前
jwt使用简介
java·jwt
顶点多余8 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
AI人工智能+电脑小能手8 小时前
大白话说Java设计模式-46-责任链模式(业务实战篇)
java·设计模式·责任链模式·风控校验·servlet filter·spring interceptor·链式处理
jay神8 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
2601_962066499 小时前
【Sql Server】Update中的From语句,以及常见更新操作方式
android·java·数据库
yaoxin52112310 小时前
507. Java 反射 - 在 BeanFactory 中实现依赖注入
java·开发语言
OPEN-F10 小时前
C++模板教程:变参模板、折叠表达式与SFINAE
java·开发语言·c++
2601_9620722111 小时前
SpringMVC进阶(自定义拦截器以及异常处理)
java