当执行 java -jar springBootDemo.jar 后发生了什么 —— 2.从JarLauncher到应用启动类

前言

上一篇文章分析了Jar包里面包含什么,并且说到MANIFES.MF里面的其中一个配置为Main-Class: org.springframework.boot.loader.JarLauncher,但是由于源代码在GitHub上,而国内网速很慢,下载好之后继续看看JarLauncher做了什么。

其中很多细节都讲的比较粗糙,也是第一次遇到这些知识点,有时间以后补充。主要是梳理启动的流程。

分析JarLauncher类以及其相关类

如下,通过IDEA分析到 JarLauncher类 的依赖情况
当执行 java -jar springBootDemo.jar 后发生了什么 —— 2.从JarLauncher到应用启动类_第1张图片
其中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-Classmain()方法,即应用启动类的main()方法。

你可能感兴趣的:(Java,SpringBoot)