SpringBoot应用为什么能直接java -jar运行?

首先明确一点,普通jar包是不能直接运行的,比如工具类jar
要能运行,至少得要一个main函数作为入口
SpringBoot应用确实有个main函数,那么问题来了,java -jar是怎么找到这个main函数运行的?

先说答案
因为引入了spring-boot-maven-plugin插件

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

如果不使用这个插件,将SpringBoot应用打个jar包
target目录产出文件如下
在这里插入图片描述

注意此时的jar体积为3202KB,内容只包含了项目中的代码和resources下的文件
在这里插入图片描述

尝试java -jar运行,报错没有主清单属性
在这里插入图片描述

这时把spring-boot-maven-plugin插件加上,再次打包
target目录如下
在这里插入图片描述
jar包体积变大了!另外多出一个jar.origin,大小和之前jar包的一样
当然我们只需要关系jar即可
查看jar包中的内容,不仅包含了自己写的类(classes),还将依赖的第三方jar包全部装了进来(lib),这种jar被称为fat jar
在这里插入图片描述
这时使用java -jar命令,成功运行!
在这里插入图片描述

按照常规思路,一定是要找到main方法所在的这个类,然后去运行里面的main方法
其实,当执行java -jar时,会自动去找一个叫MANIFEST.MF的文件,然后根据其中的Main-Class找到入口类,并执行其中的main方法
在这里插入图片描述

而SpringBoot的jar包的这个文件,就是spring-boot-maven-plugin这个插件生成的
这个文件中的内容如下

Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Archiver-Version: Plexus Archiver
Built-By: yimin
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.example.k8sdemo.K8sDemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.6.13
Created-By: Apache Maven 3.8.8
Build-Jdk: 1.8.0_392
Main-Class: org.springframework.boot.loader.JarLauncher

但是问题又来了,Main-Class居然不是我们自己写的那个类
而是org.springframework.boot.loader.JarLauncher

想要查看这个类,先引入以下依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
</dependency>

JarLauncher的main函数以及方法调用如下

public static void main(String[] args) throws Exception {
	new JarLauncher().launch(args);
}

protected void launch(String[] args) throws Exception {
	if (!isExploded()) {
		JarFile.registerUrlProtocolHandler();
	}
	ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
	String jarMode = System.getProperty("jarmode");
	String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
	launch(args, launchClass, classLoader);
}


@Override
protected String getMainClass() throws Exception {
	String mainClass = getProperty(MAIN, "Start-Class");
	if (mainClass == null) {
		throw new IllegalStateException("No '" + MAIN + "' or 'Start-Class' specified");
	}
	return mainClass;
}


protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
	Thread.currentThread().setContextClassLoader(classLoader);
	createMainMethodRunner(launchClass, args, classLoader).run();
}


protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
	return new MainMethodRunner(mainClass, args);
}

public void run() throws Exception {
	Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
	Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
	mainMethod.setAccessible(true);
	mainMethod.invoke(null, new Object[] { this.args });
}

总结一下就是

  1. 创建一个自定义的类加载器LaunchedURLClassLoader
  2. 使用这个自定义的ClassLoader去加载我们自己写的main方法类,这个类由MANIFEST.MF中Start-Class定义
  3. 反射获取main方法,然后执行
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值