java开发读取嵌套jar包中的文件

 更新时间:2023年06月02日 09:11:00   作者:Mzoro  
这篇文章主要为大家介绍了java开发读取嵌套jar包中的文件方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

读取 jar 包中的 jar 文件

例如有一个 Jar 包 A.jar, 他的目录文件如下图

A.jar
    |--B.jar
    |--Test.class
    |--.....

通过 new JarFile(A.jar) 可以等到 A.jar 对应的对象,可以遍例 A.jar 中的所有文件,Jar 包中的文件以 JarEntry 的形式保存数据 ,全码大致如下:

     public void testJar() throws IOException {
        JarFile jarFile = new JarFile("C:\\Users\\Mzoro\\Desktop\\operation-1.1.jar");
        System.out.println(jarFile.getName());
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            System.out.println(entry.getAttributes());
            System.out.println(name);
        }
     }

但是 如果想继续遍历 B.jar 中的文件就不行了,需要其他方法,有一个活生生的例子是 spring-boot 打包后的 jar 的运行过程

对应的 java 类的大致说明

一、嵌套 jar 的数据与信息获取方面

Archive,对 jar 包,或者目录的抽象

对 jar 包的抽象就是常见的,将 spring-boot 工程发布成可执行 jar 包,和嵌套其中的 jar 包与或目录,具体实现是 org.springframework.boot.loader.archive.JarFileArchive;可以通过 JarFileArchive 实例获取它的子目录或者嵌套的 jar

org.springframework.boot.loader.Launcher, 真正的 springboot 启动类

这是一个抽象类,作用如下

创建具体的 Archive 实例( Archive createArchive()),JarFileArchive 还是 WarFileArchive, 具体是通过 class 文件的协议名来判定具体实例了。如果 jar 包启动,class 文件 url 前面的协议是以 jar:file 开头的;如果是 war 包,因为窗口会将 war 解压之后 再启动,所以 class 文件 url 的协议是 file://

创建上下文的 ClassLoader; 用于加载嵌套包中的 class 与 classes 文件夹中的 class。为什么要设置上下文 classLoader 呢?因为启动 springboot 的 jar 包时的 classpath 只有 jre 环境与 springboot 的 jar 包,如果用启动 Launcher 的 ClassLoader 会找不到类,所以要设置上下文 ClassLoader 为 LanuchedURLClassLoader

这个类声明了一个 abstract List<Archive> getClassPathArchives() 方法,抽象的,目的是返回 ClassPath 下的 jar 包或者目录,为什么设置为 abstract 呢?因为 war 与 jar 的运行时依赖的 lib 是在不同目录下的,class 也在不同目录下,同时还需要过滤掉一些不必要的 jar 包或者 war 包中的东西,比如 MANIFEST.MF 文件对加载类是没有用的,所有 Archive 集合中没有必要包含它。这个方法的返回值会在构造 LancherURLClassLoader 时传入,在 findClass 时 会在这些 Archive 代表的目录或者文件中查找 Class 文件

org.springframework.boot.loader.jar.JarFile

这个类继承自 java.util.jar.JarFile, 主要重写的方法 Enumeration<java.util.jar.JarEntry> entries(); 它对应的是 springboot jar 中嵌套的 jar , 这个类的主要作用是在构造时创建一个 JarFileEntries,这个类主要重写了 entries () 方法,而这个方法返回的 Enumeration 是依靠 JarFile 持有的 JarFileEnties 获得的

private JarFile(RandomAccessDataFile rootFile, String pathFromRoot,
			RandomAccessData data, JarEntryFilter filter, JarFileType type)
			throws IOException {
		super(rootFile.getFile());
		this.rootFile = rootFile;
		this.pathFromRoot = pathFromRoot;
		CentralDirectoryParser parser = new CentralDirectoryParser();
		this.entries = parser.addVisitor(new JarFileEntries(this, filter));
		parser.addVisitor(centralDirectoryVisitor());
		this.data = parser.parse(data, filter == null);
		this.type = type;
	}

org.springframework.boot.loader.jar.JarFileEntries

这个类的作用非常重要,它代表一个 jar 包中的所有 Entries, 并且这个类在构建时就保存了这个 jar 包中所有 Entry 的文件流信息,所有在通过这个类的对象获取具体的 JarEnty 对象时,JarEnty 对象就可以包含 entry 对应的文件的真正的流数据。在 definedClass 方法的入参,byte [] 是一个必须的参数

个人觉得难就难在这里,如何计算 jar 包中每个文件的流的偏移量,文件大小等这些信息

压缩包文件格式

二、ClassLoader 方面

  • LaunchedURLClassLoader

    它继承自 URLClassLoader,这个类相对 LaunchedURLClassLoader 没有太大区别,主要的区别在于对包的定义,因为在定义包时要从嵌套 jar 中获取 MANIFEST.MF 信息

  • org.springframework.boot.loader.jar.Handler

    因为 URLClassLoader 在获取 Class 文件时需要通过 URL 对象来获取,而这个 url 具体如何获取(或者说打开 Connection),可以指定 Handler,org.springframework.boot.loader.jar.Handler 就是为了打开嵌套 jar 连接延生的; 它是实现了 java.net.URLStreamHandler 的类,URLStreamHandelr 只有一个抽象方法,就是 URLConnection openConnection(URL url)

  • JarURLConnection

    可以通过这个类获取 InputStream 了,有了 InputStream 就可以等到 definedClass 所需的 byte [] 参数,而这个 JarURLConnection 获取 InputStream 的方法是通过构建 JarURLConnection 时的 JarFile 来获取的,JarFile 获取 InputStream 的方法是通过其持有的 JarFileEntries 来获取的,JarFileEntries 的获取方法就是读取 jar 包的偏移量读取二进制数据

总结

看了一通代码最后感觉还是不能自己实现,难点在于读取嵌套 jar 包流的问题上在

疑问

代码上感觉 spring-boot-loader 只处理了一层嵌套,不知道能不能处理多层的,当然,可能也没有人这么用;如果可以的话,那么除了 springboot 工程,其他工程有没有可能也使用这种方式进行打包并进行任意层的嵌套呢?感觉好蠢的想法

参考

springboot 加载 class 方法

压缩包文件格式

以上就是java开发读取嵌套jar包中的文件的详细内容,更多关于java读取嵌套jar包文件的资料请关注脚本之家其它相关文章!

相关文章

  • Java8(291)之后禁用了TLS1.1使JDBC无法用SSL连接SqlServer2008的解决方法

    Java8(291)之后禁用了TLS1.1使JDBC无法用SSL连接SqlServer2008的解决方法

    这篇文章主要介绍了Java8(291)之后禁用了TLS1.1使JDBC无法用SSL连接SqlServer2008的解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Java 对接飞书多维表格使用详解(微服务)

    Java 对接飞书多维表格使用详解(微服务)

    本文详细介绍了如何基于飞书开放平台在微服务项目中操作飞书多维表格,包括应用创建、授权、多维表数据操作(新增、查询、删除)以及Java SDK实现等步骤,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • Java分布式ID中Snowflake雪花算法应用实现

    Java分布式ID中Snowflake雪花算法应用实现

    Snowflake算法作为一种高效且易于实现的分布式ID生成方案,能够很好地满足分布式系统中对全局唯一ID的需求,本文就来介绍一下Java分布式ID中Snowflake雪花算法应用实现,感兴趣的可以了解一下
    2024-07-07
  • SpringBoot日志进阶实战之Logback配置经验和方法

    SpringBoot日志进阶实战之Logback配置经验和方法

    本文给大家介绍在SpringBoot中使用Logback配置日志的经验和方法,并提供了详细的代码示例和解释,包括:滚动文件、异步日志记录、动态指定属性、日志级别、配置文件等常用功能,覆盖日常Logback配置开发90%的知识点,感兴趣的朋友跟随小编一起看看吧
    2023-06-06
  • springboot整合swagger3报Unable to infer base url错误问题

    springboot整合swagger3报Unable to infer base&nbs

    这篇文章主要介绍了springboot整合swagger3报Unable to infer base url错误问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • SpringBoot搭建Dubbo项目实现斐波那契第n项详解

    SpringBoot搭建Dubbo项目实现斐波那契第n项详解

    这篇文章主要讲解了“SpringBoot+Dubbo怎么实现斐波那契第N项”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习吧
    2022-06-06
  • 解决IDEA service层跳转实现类的快捷图标消失问题

    解决IDEA service层跳转实现类的快捷图标消失问题

    这篇文章主要介绍了解决IDEA service层跳转实现类的快捷图标消失问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Java 使用 Graphql 搭建查询服务详解

    Java 使用 Graphql 搭建查询服务详解

    这篇文章主要介绍了Java 使用 Graphql 搭建查询服务详解的相关资料,需要的朋友可以参考下
    2016-12-12
  • Java线程和操作系统线程的关系解读

    Java线程和操作系统线程的关系解读

    这篇文章主要介绍了Java线程和操作系统线程的关系解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • 基于Java8 函数式接口理解及测试

    基于Java8 函数式接口理解及测试

    下面小编就为大家带来一篇基于Java8 函数式接口理解及测试。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08

最新评论