服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

2022-12-07 20:30Java极客技术鸭血粉丝Tang Java教程

通过上面的内容我们知道了当我们在执行 java -jar​ 的时候,根据 java​ 官方规范会引导 jar​ 包里面 MANIFEST.MF​ 文件中的 Main-Class​ 属性对应的启动类,该启动类中必须包含 main() 方法。

大家都知道我们常用的 SpringBoot 项目最终在线上运行的时候都是通过启动 java -jar xxx.jar 命令来运行的。

那你有没有想过一个问题,那就是当我们执行 java -jar 命令后,到底底层做了什么就启动了我们的 SpringBoot 应用呢?

或者说一个 SpringBoot 的应用到底是如何运行起来的呢?今天阿粉就带大家来看下。

认识 jar

在介绍 java -jar 运行原理之前我们先看一下 jar 包里面都包含了哪些内容,我们准备一个 SpringBoot 项目,通过在 https://start.spring.io/ 上我们可以快速创建一个 SpringBoot 项目,下载一个对应版本和报名的 zip 包。

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

下载后的项目我们在 pom 依赖里面可以看到有如下依赖,这个插件是我们构建可执行 jar 的前提,所以如果想要打包成一个 jar 那必须在 pom 有增加这个插件,从 start.spring.io 上创建的项目默认是会带上这个插件的。

 

<build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> plugins> build>

 

接下来我们执行 mvn package,执行完过后在项目的 target 目录里面我们可以看到有如下两个 jar 包,我们分别把这两个 jar 解压一下看看里面的内容,.original 后缀的 jar 需要把后面的 .original 去掉就可以解压了。jar 文件的解压跟我们平常的 zip 解压是一样的,jar 文件采用的是 zip 压缩格式存储,所以任何可以解压 zip 文件的软件都可以解压 jar 文件。

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

解压过后,我们对比两种解压文件,可以发现,两个文件夹中的内容还是有很大区别的,如下所示,左侧是 demo-jar-0.0.1-SNAPSHOT.jar 右侧是对应的 original jar。

其中有一些相同的文件夹和文件,比如 META-INF,application.properties 等,而且我们可以明显的看到左侧的压缩包中有项目需要依赖的所有库文件,存放于 lib 文件夹中。

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

所以我们可以大胆的猜测,左侧的压缩包就是 spring-boot-maven-plugin 这个插件帮我们把依赖的库以及相应的文件调整了一下目录结构而生成的,事实其实也是如此。

java -jar 原理

首先我们要知道的是这个 java -jar 不是什么新的东西,而是 java 本身就自带的命令,而且 java -jar 命令在执行的时候,命令本身对于这个 jar 是不是 SpringBoot 项目是不感知的,只要是符合 Java 标准规范的 jar 都可以通过这个命令启动。

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

而在 Java 官方文档显示,当 -jar 参数存在的时候,jar 文件资源里面必须包含用 Main-Class 指定的一个启动类,而且同样根据规范这个资源文件 MANIFEST.MF 必须放在 /META-INF/ 目录下。对比我们上面解压后的文件,可以看到在左侧的资源文件 MANIFEST.MF 文件中有如图所示的一行。

![](/Users/silence/Library/Application Support/typora-user-images/image-20221206214011822.png)

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

可以看到这里的 Main-Class 属性配置的是 org.springframework.boot.loader.JarLauncher,而如果小伙伴更仔细一点的话,会发现我们项目的启动类也在这个文件里面,是通过 Start-Class 字段来表示的,Start-Class 这个属性不是 Java 官方的属性。

由此我们先大胆的猜测一下,当我们在执行 java -jar 的时候,由于我们的 jar 里面存在 MANIFEST.MF 文件,并且其中包含了 Main-Class 属性且配置了 org.springframework.boot.loader.JarLauncher 类,通过调用 JarLauncher 类结合 Start-Class 属性引导出我们项目的启动类进行启动。接下来我们就通过源码来验证一下这个猜想。

因为 JarLauncher 类是在 spring-boot-loader 模块,所以我们在 pom 文件中增加如下依赖,就可以下载源码进行跟踪了。

 

<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-loaderartifactId> <scope>providedscope> dependency>

 

通过源码我们可以看到 JarLauncher 类的代码如下

package org.springframework.boot.loader; import org.springframework.boot.loader.archive.Archive; import org.springframework.boot.loader.archive.Archive.EntryFilter; public class JarLauncher extends ExecutableArchiveLauncher { static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> { if (entry.isDirectory()) { return entry.getName().equals("BOOT-INF/classes/"); } return entry.getName().startsWith("BOOT-INF/lib/"); }; public JarLauncher(){ } protected JarLauncher(Archive archive){ super(archive); } @Override
 protected boolean isPostProcessingClassPathArchives(){ return false; } @Override
 protected boolean isNestedArchive(Archive.Entry entry){ return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry); } @Override
 protected String getArchiveEntryPathPrefix(){ return "BOOT-INF/"; } public static void main(String[] args) throws Exception { new JarLauncher().launch(args); } }

其中有两个点我们可以关注一下,第一个是这个类有一个 main 方法,这也是为什么 java -jar 命令可以进行引导的原因,毕竟 java 程序都是通过 main 方法进行运行的。其次是这里面有两个路径 BOOT-INF/classes/ 和 BOOT-INF/lib/ 这两个路径正好是我们的源码路径和第三方依赖路径。

而 JarLauncher 类里面的 main() 方法主要是运行 Launcher 里面的 launch() 方法,这几个类的关系图如下所示:

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

跟着代码我们可以看到最终调用的是这个 run() 方法。

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

而这里的参数 mainClass 和 launchClass 都是通过通过下面的逻辑获取的,都是通过资源文件里面的 Start-Class 来进行获取的,这里正是我们项目的启动类,由此可以看到我们上面的猜想是正确的。

当我们的执行 Java -jar xxx.jar 的时候底层到底做了什么?

扩展

上面的类图当中我们还可以看到除了有 JarLauncher 以外还有一个 WarLauncher 类,确实我们的 SpringBoot 项目也是可以配置成 war 进行部署的。我们只需要将打包插件里面的 jar 更换成 war 即可。大家可以自行尝试重新打包解压进行分析,这里 war 包部署方式只研究学习就好了,SpringBoot 应用还是尽量都使用 Jar 的方式进行部署。

总结

通过上面的内容我们知道了当我们在执行 java -jar 的时候,根据 java 官方规范会引导 jar 包里面 MANIFEST.MF 文件中的 Main-Class 属性对应的启动类,该启动类中必须包含 main() 方法。

而对于我们 SpringBoot 项目构建的 jar 包,除了 Main-Class 属性外还会有一个 Start-Class 属性绑定的是我们项目的启动类,当我们在执行 java -jar 的时候优先引导的是 org.springframework.boot.loader.JarLauncher#main 方法,该方法内部会通过引导 Start-Class 属性来启动我们的应用代码。

通过上面的分析相比大家对于 SpringBoot 是如何通过 java -jar 进行启动了有了一个详细的了解,下次再有人问 SpringBoot 项目是如何启动的,请把这篇文章转发给他。

原文地址:https://mp.weixin.qq.com/s/VyKtg1bG9Xj_1gL90ylCNw

延伸 · 阅读

精彩推荐
  • Java教程log4j.properties 配置(实例讲解)

    log4j.properties 配置(实例讲解)

    下面小编就为大家带来一篇log4j.properties 配置(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    xieshuang4592020-12-18
  • Java教程mybatis plus 的动态表名的配置详解

    mybatis plus 的动态表名的配置详解

    这篇文章主要介绍了mybatis plus 的动态表名的配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们...

    团子是个傻子6522020-09-03
  • Java教程Java小白第一次就能看懂的网络编程

    Java小白第一次就能看懂的网络编程

    网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。本文介绍了一些网络编程基础的概念,并用Java来实现TCP和UDP的Socke...

    扛麻袋的少年5722021-12-09
  • Java教程Java排序算法总结之插入排序

    Java排序算法总结之插入排序

    这篇文章主要介绍了Java排序算法总结之插入排序,较为详细的分析了插入排序的原理与java实现技巧,需要的朋友可以参考下 ...

    一羽清宁5132019-12-17
  • Java教程RocketMQ 延时级别配置方式

    RocketMQ 延时级别配置方式

    这篇文章主要介绍了RocketMQ 延时级别配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    叮当快跑11872021-09-25
  • Java教程Java中一些关键字的使用技巧总结

    Java中一些关键字的使用技巧总结

    这篇文章主要介绍了Java中一些关键字的使用技巧总结,其中重点讲述了this和super两个关键字的用法,需要的朋友可以参考下 ...

    leizhimin4922020-01-04
  • Java教程VMware虚拟机下hadoop1.x的安装方法

    VMware虚拟机下hadoop1.x的安装方法

    这篇文章主要为大家详细介绍了VMware虚拟机下hadoop1.x的安装方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    W.Yentl7412021-01-06
  • Java教程java生成json实现隐藏掉关键属性

    java生成json实现隐藏掉关键属性

    这篇文章主要介绍了java生成json实现隐藏掉关键属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    猿人启示录10562022-08-17