当我们的执行 java -jar xxx.jar 的时候底层到底做了什么?
大家都知道我们常用的 SpringBoot
项目最终在线上运行的时候都是通过启动 java -jar xxx.jar
命令来运行的。
那你有没有想过一个问题,那就是当我们执行 java -jar
命令后,到底底层做了什么就启动了我们的 SpringBoot
应用呢?
或者说一个 SpringBoot
的应用到底是如何运行起来的呢?今天阿粉就带大家来看下。
认识 jar
在介绍 java -jar
运行原理之前我们先看一下 jar
包里面都包含了哪些内容,我们准备一个 SpringBoot
项目,通过在 https://start.spring.io/ 上我们可以快速创建一个 SpringBoot
项目,下载一个对应版本和报名的 zip
包。
下载后的项目我们在 pom
依赖里面可以看到有如下依赖,这个插件是我们构建可执行 jar
的前提,所以如果想要打包成一个 jar
那必须在 pom
有增加这个插件,从 start.spring.io
上创建的项目默认是会带上这个插件的。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
接下来我们执行 mvn package
,执行完过后在项目的 target
目录里面我们可以看到有如下两个 jar
包,我们分别把这两个 jar
解压一下看看里面的内容,.original
后缀的 jar 需要把后面的 .original
去掉就可以解压了。jar
文件的解压跟我们平常的 zip
解压是一样的,jar
文件采用的是 zip
压缩格式存储,所以任何可以解压 zip
文件的软件都可以解压 jar
文件。
解压过后,我们对比两种解压文件,可以发现,两个文件夹中的内容还是有很大区别的,如下所示,左侧是 demo-jar-0.0.1-SNAPSHOT.jar
右侧是对应的 original jar
。
其中有一些相同的文件夹和文件,比如 META-INF
,application.properties
等,而且我们可以明显的看到左侧的压缩包中有项目需要依赖的所有库文件,存放于 lib
文件夹中。
所以我们可以大胆的猜测,左侧的压缩包就是 spring-boot-maven-plugin
这个插件帮我们把依赖的库以及相应的文件调整了一下目录结构而生成的,事实其实也是如此。
java -jar 原理
首先我们要知道的是这个 java -jar
不是什么新的东西,而是 java
本身就自带的命令,而且 java -jar
命令在执行的时候,命令本身对于这个 jar
是不是 SpringBoot
项目是不感知的,只要是符合 Java
标准规范的 jar
都可以通过这个命令启动。
而在 Java
官方文档显示,当 -jar
参数存在的时候,jar
文件资源里面必须包含用 Main-Class
指定的一个启动类,而且同样根据规范这个资源文件 MANIFEST.MF
必须放在 /META-INF/
目录下。对比我们上面解压后的文件,可以看到在左侧的资源文件 MANIFEST.MF
文件中有如图所示的一行。
![](/Users/silence/Library/Application Support/typora-user-images/image-20221206214011822.png)
可以看到这里的 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.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
<scope>provided</scope>
</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()
方法,这几个类的关系图如下所示
跟着代码我们可以看到最终调用的是这个 run()
方法
而这里的参数 mainClass
和 launchClass
都是通过通过下面的逻辑获取的,都是通过资源文件里面的 Start-Class
来进行获取的,这里正是我们项目的启动类,由此可以看到我们上面的猜想是正确的。
扩展
上面的类图当中我们还可以看到除了有 JarLauncher
以外还有一个 WarLauncher
类,确实我们的 SpringBoot
项目也是可以配置成 war
进行部署的。我们只需要将打包插件里面的 jar
更换成 war
即可。大家可以自行尝试重新打包解压进行分析,这里 war
包部署方式只研究学习就好了,SpringBoot
应用还是尽量都使用 Jar
的方式进行部署。
总结
通过上面的内容我们知道了当我们在执行 java -jar
的时候,根据 java
官方规范会引导 jar
包里面 MANIFEST.MF
文件中的 Main-Class
属性对应的启动类,该启动类中必须包含 main()
方法。
而对于我们 SpringBoot
项目构建的 ja
r 包,除了 Main-Class
属性外还会有一个 Start-Class
属性绑定的是我们项目的启动类,当我们在执行 java -jar
的时候优先引导的是 org.springframework.boot.loader.JarLauncher#main
方法,该方法内部会通过引导 Start-Class
属性来启动我们的应用代码。
通过上面的分析相比大家对于 SpringBoot
是如何通过 java -jar
进行启动了有了一个详细的了解,下次再有人问你 SpringBoot
项目是如何启动的,请把这篇文章转发给他。如果大家觉得我们的文章有帮助,欢迎点赞分享评论转发,一键三连。
更多优质内容欢迎关注公众号【Java 极客技术】,我准备了一份面试资料,回复【bbbb07】免费领取。希望能在这寒冷的日子里,帮助到大家。
当我们的执行 java -jar xxx.jar 的时候底层到底做了什么?的更多相关文章
- Linux环境下执行java -jar xxx.jar命令如何让springboot项目在后台运行
段落引用> 由于springboot内置了tomcat容器,我们通常会把项目打成jar或者war后直接使用java -jar xxx.jar命令去运行程序,但是当前ssh窗口被锁定或者按下ctr ...
- [原创] 如何用Eclispe调试java -jar xxx.jar 方式执行的jar包
有时候,我们经常会需要调试 java -jar xxx.jar方式运行的代码,而不是必须在Eclipse中用Debug或者Run的方式运行.比如我们拿到的SourceCode不完整.Java提供了一种 ...
- 【转】如何用Eclispe调试java -jar xxx.jar 方式执行的jar包
原文地址:https://www.cnblogs.com/zzpbuaa/p/5443269.html 有时候,我们经常会需要调试 java -jar xxx.jar方式运行的代码,而不是必须在Ecl ...
- Java:Linux上java -jar xxx.jar&java -cp 区别
java -cp java -cp 和 -classpath 一样,是指定类运行所依赖其他类的路径,通常是类库和jar包,需要全路径到jar包,多个jar包之间连接符:window上分号“;”.Lin ...
- 使用idea 调试java -jar xxx.jar方式启动
今日思语:希望是什么?希望就是 你还在挣扎中... idea是一个功能强大的java开发工具,可以很方便的帮助开发人员进行开发工作. 1.有时我们通过使用java -jar xxx.jar方式启动可执 ...
- MySQL实战 | 01-当执行一条 select 语句时,MySQL 到底做了啥?
原文链接:当执行一条 select 语句时,MySQL 到底做了啥? 也许,你也跟我一样,在遇到数据库问题时,总时茫然失措,想重启解决问题,又怕导致数据丢失,更怕重启失败,影响业务. 就算重启成功了, ...
- Java:Linux上java -jar xxx.jar命令执行jar包时出现Error: Invalid or corrupt jarfile xxx.jar解决方案
背景: 从ftp上上传jar包到linux上,之后在linux上通过ftp命令下载jar包文件,开始执行Java-jar,一直提示错误:Error: Invalid or corrupt jarfil ...
- java -jar xxx.jar
之前用MyEclipse做了一个可执行jar,点击就可运行的. 今天突然不好用了,错误是: could not find the main class C:\123\abc.jar.Program w ...
- java -jar -Xbootclasspath/a:/xxx/config xxx .jar 和 java -jar xxx .jar 的区别
1.如果有用Xbootclasspath的话则config的文件会直接覆盖jar里面的resouces文件,覆盖application.yml ,也会覆盖logback-spring.xml ,比如j ...
- java -jar start.jar和nohup java -jar xxx.jar > test.log &的区别
nohup用在什么地方? KD3EE49RD38
随机推荐
- Fielddata is disabled on text fields by default Set fielddata=true on [service.address]
2个字段的: PUT metricbeat-7.3.0/_mapping { "properties": { "service": { "proper ...
- centos使用Yum安装postgresql 13
rpm源安装 yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat ...
- Beats:使用Elastic Stack对Redis监控
- css语言
css:样式表.级联样式表.层叠样式表 css写在style标签里面,放在head标签中:大括号中写键值对语法 color:文字颜色 Font-family:字体 Font-size:字号 text- ...
- js移除style样式
removeAttribute() 例: <button @click="edit" type="button" disabled id="bt ...
- Pyhton实践项目之(一)五子棋人机对战
1 """五子棋之人机对战""" 2 3 import random 4 import sys 5 6 import pygame 7 im ...
- 分布式存储系统之Ceph集群CephX认证和授权
前文我们了解了Ceph集群存储池操作相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16743611.html:今天我们来聊一聊在ceph上认证和授权的 ...
- numpy中的一些常用的关键字用法
1.np.full() 原型:numpy.full(shape, fill_value, dtype=None, order='C') eg: 2.np.flatten():该函数返回一个折叠成一维的 ...
- gets,fgets,puts,fputs,scanf,printf的作用,联系和区别
转载: https://blog.csdn.net/lc10915819/article/details/12747943
- golang单元测试一(简单函数测试)
0.1.索引 https://blog.waterflow.link/articles/1663688140724 1.简介 单元测试是测试代码.组件和模块的单元函数.单元测试的目的是清除代码中的错误 ...