获取jar包中jar包中的class

问题

上篇写了获取jar包中的class文件,在IDEA运行的好好的, 打个包丢服务器上就跑不了了。 原因就是打成jar包之后获取到的class文件路径变成/demo/xxx.jar!/WEB-INF/lib/yyy.jar!/com/demo/zzz.class了,在IDEA中只要读取yyy.jar中的class就行,现在要读取xxx.jar包中WEB-INF/lib下yyy.jar中的class文件, 所以今天再来说说获取jar包中的jar包中的class文件。

分析

一开始的想法是读取到的文件如果是jar包就再读取这个jar包中的文件,可是发现JarFile和JarEntry中并没有方法能返回jar文件,也不能获取内层文件。Url也不能直接获取两层jar包。

那么我们打的jar包在服务器使用java -jar启动的时候是这么读取jar包内的文件呢。来解压jar包查看MANIFEST.MF文件:

Manifest-Version: 1.0
Implementation-Title: application
Implementation-Version: 2.0-SNAPSHOT
Start-Class: com.demo.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.2.4.RELEASE
Created-By: Maven Archiver 3.4.0
Implementation-Vendor: Yuanting information technology co.,LTD
Main-Class: org.springframework.boot.loader.JarLauncher

看这个Main-Class入口类是org.springframework.boot.loader.JarLauncher,执行流程大致如下,有兴趣的可以深入了解一下。

JarLauncher.main
创建类加载器 LaunchedURLClassLoader
加载位置Jar包根目录下BOOT/class BOOT/lib
class拆解方案 去掉 BOOT/class 前缀直接得到类
lib拆解方案 提供JarFile--重写了java.util.jar.JarFile
lib类类文件拆解方案 提供JarEntry--重写了 java.util.jar.JarEntry
用户class.main

解决

复制粘贴工程师就不了解那么多了,直接看解决方案。

首先添加org.springframework.boot.loader依赖:

<!-- spring-boot-loader-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-loader</artifactId>
  <version>2.2.4.RELEASE</version>
</dependency>

精华就在于重写的JarFile,用它读取jar文件就行了:

public void testJarLaunch() throws IOException, ClassNotFoundException {
        String path = "jar:file:/IdeaProjects/demo-application/target/demo-application-1.0-SNAPSHOT.jar!";
        URL url = new URL(path);
        Handler handler = new Handler();
        org.springframework.boot.loader.jar.JarFile root = handler.getRootJarFileFromUrl(url);

        JarFileArchive archive = new JarFileArchive(root);
  		//灵魂方法getNestedArchives获取嵌套的jar等文件,参数是个EntryFilter,过滤条件
        List<Archive> list = archive.getNestedArchives(entry -> {
            System.out.println(entry.getName());
            return entry.getName().endsWith("BOOT-INF/lib/app-core-1.0-SNAPSHOT.jar");
        });

  		//list里就是每个jar了
        for (Archive entries : list) {
          	//获取jar里的内容
            Iterator<Archive.Entry> it = entries.iterator();
            while (it.hasNext()){
                Archive.Entry entry = it.next();
                String name = entry.getName();
                System.out.println(name);
            }
        }

    }

懒得说了有兴趣的参考这篇:SpringBoot的main函数运行之前都发生了什么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值