java中常见jar包形式的联系和结构解析

avatar
SE

一、背景

在学习使用maven来打包jar、或者使用springboot打包插件的时候,经常会看到jar、executable-jar、uberjar/fatjar、jar-with-dependencies、jar-in-jar/nested-jars等jar包形式,那么,这些jar包有之间联系吗?他们的结构有什么区别吗?

二、名词解释

pre:严格意义上讲,jar是一种打包压缩的文件格式,后缀是.jar,与.zip是差不多的概念,本文所列举的几个jar包名,都是在java使用过程中出现的具有特定形式要求jar包。

1.jar

特指在java程序中可被用作依赖的jar包,java可以直接读取jar包中的class文件
参考:docs.oracle.com/javase/8/do…

2.executable-jar:

当jar包被用来当作java直接启动“介质”的时候,不仅要求可以直接读取jar包中的class文件 ,也要求提供程序入口的相关信息,这样jar包就成了executable-jar
参考:docs.oracle.com/javase/tuto…

3.uber-jar/fat-jar

这里需要区分开 应用程序的类应用程序的依赖 两个概念,当一个jar包中不仅包含了应用程序本身的类,也包含了应用程序所需的依赖时,我们称之为fat-jar、也叫uber-jar

4.jar-with-dependencies:

特指maven打包插件maven-assembly-plugin中的默认配置 jar-with-dependencies ,该插件配置打包时,会同时将业务代码及其依赖一同打包进最终jar包中
参考:maven.apache.org/plugins/mav…

5.jar-in-jar/nested-jars

jar-in-jar貌似只有中文资料,官方名称应该是nested-jars,意思就是jar包中包的jar包,特指springboot的打包插件spring-boot-maven-plugin的打包结果,也会同时将业务代码及其依赖一同打包进最终jar包中
参考:maven.apache.org/plugins/mav…

PS:java程序默认是无法直接读取jar-in-jar中的依赖的

三、关系

一图胜千言: image.png

四、jar包的结构

1.jar

class文件以特定形式组合打包,能够作为java程序的依赖,被java程序直接读取,它的结构如下: image.png

PS:依赖jar包中的META-INF是可选的

2.executable-jar:

java中定义的executable-jar会在依赖jar包的基础上增加 /META-INF/MANIFEST.MF 文件,并在其中指定 Main-Class属性,也就是指明程序的main入口 image.png

PS:executable-jar中的MANIFEST.MF,除了 Main-Class 外,其它属性是可选的

3.jar-with-dependencies

将依赖直接解压到jar下的顶级目录下: image.png

4.jar-in-jar/nested-jars

将依赖以jar包的形式(不解压)直接放入到jar包中的/BOOT-INF.lib目录下: image.png

Q:既然maven已经提供了 jar-with-dependencies,为什么springboot还要搞一个 jar-in-jar/nested-jars呢?

springboot的文档里边有简要提到使用jar-in-jar/nested-jars的原因,主要就是为了解决在依赖繁杂的情况下,全部解压到jar包根目录导致的类冲突和交叉覆盖,且难以溯源的问题。 image.png

五、启动过程

1.jar-with-dependencies

直接 java -jar xxx.jar 启动即可,跟普通的 executable-jar 一致,不需要进行额外处理。
image.png

2.jar-in-jar/nested-jars

也是 java -jar xxx.jar 进行启动,但是为了读取 nested-jar 的依赖,额外包装了一层,目的主要是扩展URL协议,并且使用了自定义的LaunchedURLClassLoader ClassLoader

a.jar包结构信息:

可以看到Main-Class并不是业务的main入口,而是springboot-loader的main入口image.png

b.实际的启动流程示意:

springboot的nested-jar打包方式下的应用启动流程.jpg

PS:LaunchedURLClassLoader 一般也会被称作springboot classloader,网上已有很多资料进行分析,这里不赘述。