前提
关于springboot的类加载原理和spring-boot-loader
的源码解析,网上已有很多的很棒的文章了。我一直相信对于技术原理的获取,代码层面的话一定是动眼比不上动手,debug
跟着源码运行走一遍,效果往往是很棒的。同理,对于spring-boot-loader
的原理,源码的掌握,其原理和debug
方式参考:springboot jar包可运行,debug告诉你怎么运行的
本文想要讲述的可能要更近一步了。带着一些疑问开始我们的学习
- 1、程序是从哪进入
JarLauncher.main()
方法的 - 2、
java -jar xxx-executable.jar
java
有什么规定吗 - 3、
jvm
层面是如何进入到java
程序的,连接点在哪里
》本文力求专注和精简,希望你有所收获和想法
正文
-jar
的规矩
其实,java
对-jar
定义了一些规则,只要符合这个规则,你做啥都行,怎么做都行。其中的一个是:jar
中需要一个META-INF/MAINFEST.MF
文件,且文件中Main-Class manifest header
。官网的说明如下
所以,我们解压一个xxx-executable.jar
,会看到META-INF/MAINFEST.MF
的文件及其内容
正是因为有了Main-Class: org.springframework.boot.loader.JarLauncher
,所以,当java -jar xxx-executable.jar
时,程序会进入org.springframework.boot.loader.JarLauncher
执行main
方法。到这里我又有了疑问,那是从哪进入JarLauncher.main
方法的呢
程序是怎么进入JarLauncher.main
方法的呢
这个时候想到了jvm
。应该是从jvm
进去到了JarLauncher.main
,那么怎么验证呢。
我们通过CLion
查看jvm
源码,试图找到这个答案。我们通过全局搜索Main-Class
,Manifest
这些关键词来定位所在的文件,并打上相应的debug
端点。如下图
然后我们启动debug
模式启动jvm
,启动时配置上我们的应用jar
包:-jar xxx-executable.jar
,如下图
现在我们跟着jvm
的debug
一起追下寻找Main-Class
,Manifest
的过程。
看到了JLI_ParseManifest
,从名字也看的出来这个眉目了
可以看到static const char *manifest_name = "META-INF/MANIFEST.MF"
在jvm中是个常量。所以,我们的应用xxx-executable.jar
中必须有个叫这个名的目录和文件。方法接着解析了这个文件,文件的内容和我们解压xxx-executable.jar
看到的内容一致,如下图
所以,到这Main-Class
的值就被解析出来了。下面我们看下jvm如何加载Main-Class
的值(即org.springframework.boot.loader.JarLauncher
)的。
下面图中6个部分其实将虚拟机的整个初始化和加载过程都显示出来了。在这里我们只关注JarLauncher
是如何加载初始化与JarLauncher.main
是怎么运行的,所以我们重点关注(1)、(2)、(4)、(6)
(1)LoadMainClass
- 加载main class
我们先看下LoadMainClass
的细节,看看她咋加载的。通过GetLauncherHelperClass
方法,我们终于看到了熟悉的身影:sun/launcher/LauncherHelper
这个java class
。如下图
LoadMainClass
做了三件事:
1、获取LauncherHelper
实例
2、通过checkAndLoadMain
方法使用ClassLoader.getSystemClassLoader
初始化org.springframework.boot.loader.JarLauncher class
3、makePlatformString
获取utf-8
后的string
需要特别指出的是LauncherHelper.java
。这个类的源码在jvm
中,在我们熟悉的rt.jar
是以.class
的形式出现的。我把他俩放在一张图片里对比下
(2)获取LauncherHelper
的实例
这里的实例是在(1)中初始化好的,用就好了
(4)验证和加载main
方法
这里用了类似反射的方法来获取main
方法
(6)调用LauncherHelper.main
方法
这里就是我们一直要知道的那个地方,也是文章开头部分的问题1和3的答案。即jvm
运行和java application
的连接点。利用CLion
和Idea
,利用debug
,我人为的把他俩的运行结合在一起,具象的表示出这两个点的运行,如下图
it`s time to summary
到这里,我们的疑问都找到答案了。我们从jvm
源码到java
代码,整个流程串下来。相信有人再问你springboot
的入口时,你不仅知道了是JarLauncher.main
,而且能进一步知道从哪(怎么)进入的JarLauncher.main
的。这不管是对工作,还是面试,对你都是有很大收益的。
附录
所有,从jvm
到springboot
,再到应用app
的入口,我们现在都知道了。
java.c的JavaMain方法 --> LauncherHelper.java的main方法 --> Application.java的main方法
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。