前言
上一篇文章分析了Jar包里面包含什么,并且说到MANIFES.MF
里面的其中一个配置为Main-Class: org.springframework.boot.loader.JarLauncher
,但是由于源代码在GitHub上,而国内网速很慢,下载好之后继续看看JarLauncher做了什么。
其中很多细节都讲的比较粗糙,也是第一次遇到这些知识点,有时间以后补充。主要是梳理启动的流程。
分析JarLauncher类以及其相关类
如下,通过IDEA分析到 JarLauncher类 的依赖情况
其中JarLauncher的main方法如下
- JarLauncher.java
// main 函数如下,由于继承的是Launcher类,因此launch函数具体需要看Launcher类
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
- Launcher.java
// 具体的launch方法
protected void launch(String[] args) throws Exception {
// 把"org.springframework.boot.loader"添加到"java.protocol.handler.pkgs"
// 通过 JVM 启动参数 -Djava.protocol.handler.pkgs来设置 URLStreamHandler 实现类的包路径
JarFile.registerUrlProtocolHandler();
// 创建自定义类加载器加载,加载/BOOT-INF/lib和/BOOT-INF/classes下的所有资源,交给LaunchedURLClassLoader
ClassLoader classLoader = createClassLoader(getClassPathArchives());
// 启动应用启动类。其中getMainClass()方法见ExecutableArchiveLauncher类
launch(args, getMainClass(), classLoader);
}
// launch()方法
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
// 设置当前线程的classLoader
Thread.currentThread().setContextClassLoader(classLoader);
// 继续往下,通过createMainMethodRunner,然后调用run()方法
createMainMethodRunner(mainClass, args, classLoader).run();
}
// 继续往下,看看MainMethodRunner做了什么
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
return new MainMethodRunner(mainClass, args);
}
有关
java.protocl.handler.pkgs
的知识可自行百度,暂时我也不是很懂。
有关LaunchedURLClassLoader.java
的知识可参考springboot应用启动原理(二) 扩展URLClassLoader实现嵌套jar加载
- MainMethodRunner.java
专门为main方法,写了一个启动类
// 构造函数
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = (args != null) ? args.clone() : null;
}
// 启动,使用反射原理启动main函数
public void run() throws Exception {
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke(null, new Object[] { this.args });
}
- ExecutableArchiveLauncher.java
// 该类继承了Launcher类,并实现了getMainClass()方法。
// 代码比较好读,很明显,直接从文件MANIFES.MF中读取属性Start-Class,即应用的启动类
@Override
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
mainClass = manifest.getMainAttributes().getValue("Start-Class");
}
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
}
return mainClass;
}
总结
在javar -jar 之后,通过MANIFES.MF
找到Main-Class
,以JarLauncher.java
为入口,加载所有的需要的启动资源(BOOT-INF/classes/*,BOOT-INF/lib/*
),交给自定义的类加载器,然后通过反射,启动MANIFES.MF
中定义的Start-Class
的main()
方法,即应用启动类的main()
方法。