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

相关推荐
yangmu320329 分钟前
Unity 开发过程与底层原理
java·开发语言
lexiangqicheng31 分钟前
自定义表单跨端共用机制-PC设计APP渲染
java·前端·javascript
zhikouai34 分钟前
AI辅助逆向工程链路:从二进制接入到等价性校验
java·linux·前端
随性而行3601 小时前
企业微信API接口开发实战:如何实现消息回调接收与业务事件处理?
java·前端·python·微信·机器人·企业微信
美味蛋炒饭.1 小时前
java八股面试(3)
java·开发语言·面试
IT研究室1 小时前
最新计算机毕业设计选题推荐-基于spring boot的美食配送平台-网站-文档指导-Java-springboot
java·spring boot·课程设计
霸道流氓气质1 小时前
Saga模式入门与实战:编排与协同模式、补偿设计原则与Java生产级实现
java·开发语言
江湖有缘1 小时前
Docker实战 | 使用Docker部署Readeck书签管理工具
java·docker·eureka
小毛驴8501 小时前
mvn help:effective-settings 与 mvn help:effective-pom
java·linux·前端
silvamux_1 小时前
MiniMax API 调用教程:兼容 OpenAI 写法
java·数据库·elasticsearch