🙈作者简介:练习时长两年半的Java up主
🙉个人主页:程序员老茶
🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎
📚系列专栏:Java全栈,计算机系列(火速更新中)
💭 格言:种一棵树最好的时间是十年前,其次是现在
🏡动动小手,点个关注不迷路,感谢宝子们一键三连
目录
- 课程名:Java
- 内容/作用:知识点/设计/实验/作业/练习
- 学习:Java
- SpringBoot程序打包失败处理
课程名:Java
内容/作用:知识点/设计/实验/作业/练习
学习:Java
SpringBoot程序打包失败处理
有些小伙伴打包以后执行会出现一些问题,导致程序无法正常执行,例如下面的现象
要想搞清楚这个问题就要说说.jar文件的工作机制了,知道了这个东西就知道如何避免此类问题的发生了。
搞java开发平时会接触很多jar包,比如mysql的驱动jar包,而上面我们打包程序后得到的也是一个jar文件。这个时候如果你使用上面的java -jar指令去执行mysql的驱动jar包就会出现上述不可执行的现象,而我们的SpringBoot项目为什么能执行呢?其实是因为打包方式不一样。
在SpringBoot工程的pom.xml中有下面这组配置,这组配置决定了打包出来的程序包是否可以执行。
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>
我们分别开启这段配置和注释掉这段配置分别执行两次打包,然后观察两次打包后的程序包的差别,共有3处比较明显的特征
- 打包后文件的大小不同
- 打包后所包含的内容不同
- 打包程序中个别文件内容不同
先看第一个现象,文件大小不同。带有配置时打包生成的程序包大小如下:
不难看出,带有配置的程序包体积比不带配置的大了30倍,那这里面都有什么呢?能差这么多?下面看看里面的内容有什么区别。
我们发现内容也完全不一样,仅有一个目录是一样的,叫做META-INF。打开容量大的程序包中的BOOT-INF目录下的classes目录,我们发现其中的内容居然和容量小的程序包中的内容完全一样。
原来大的程序包中除了包含小的程序包中的内容,还有别的东西。都有什么呢?回到BOOT-INF目录下,打开lib目录,里面显示了很多个jar文件。
仔细翻阅不难发现,这些jar文件都是我们制作这个工程时导入的坐标对应的文件。大概可以想明白了,SpringBoot程序为了让自己打包生成的程序可以独立运行,不仅将项目中自己开发的内容进行了打包,还把当前工程运行需要使用的jar包全部打包进来了。为什么这样做呢?就是为了可以独立运行。不依赖程序包外部的任何资源可以独立运行当前程序。这也是为什么大的程序包容量是小的程序包容量的30倍的主要原因。
再看看大程序包还有什么不同之处,在最外层目录包含一个org目录,进入此目录,目录名是org\springframework\boot\loader,在里面可以找到一个JarLauncher.class的文件,先记得这个文件。再看这套目录名,明显是一个Spring的目录名,为什么要把Spring框架的东西打包到这个程序包中呢?不清楚。
回到两个程序包的最外层目录,查看名称相同的文件夹META-INF下都有一个叫做MANIFEST.MF的文件,但是大小不同,打开文件,比较内容区别
-
小容量文件的MANIFEST.MF
Manifest-Version: 1.0 Implementation-Title: springboot_08_ssmp Implementation-Version: 0.0.1-SNAPSHOT Build-Jdk-Spec: 1.8 Created-By: Maven Jar Plugin 3.2.0
-
大容量文件的MANIFEST.MF
Manifest-Version: 1.0 Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx Implementation-Title: springboot_08_ssmp Implementation-Version: 0.0.1-SNAPSHOT Spring-Boot-Layers-Index: BOOT-INF/layers.idx Start-Class: com.itheima.SSMPApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.5.4 Created-By: Maven Jar Plugin 3.2.0 Main-Class: org.springframework.boot.loader.JarLauncher
大文件中明显比小文件中多了几行信息,其中最后一行信息是Main-Class: org.springframework.boot.loader.JarLauncher。这句话什么意思呢?如果使用java -jar执行此程序包,将执行Main-Class属性配置的类,这个类恰巧就是前面看到的那个文件。原来SpringBoot打包程序中出现Spring框架的东西是为这里服务的。而这个org.springframework.boot.loader.JarLauncher类内部要查找Start-Class属性中配置的类,并执行对应的类。这个属性在当前配置中也存在,对应的就是我们的引导类类名。
现在这组设定的作用就搞清楚了
- SpringBoot程序添加配置后会打出一个特殊的包,包含Spring框架部分功能,原始工程内容,原始工程依赖的jar包
- 首先读取MANIFEST.MF文件中的Main-Class属性,用来标记执行java -jar命令后运行的类
- JarLauncher类执行时会找到Start-Class属性,也就是启动类类名
- 运行启动类时会运行当前工程的内容
- 运行当前工程时会使用依赖的jar包,从lib目录中查找
看来SpringBoot打出来了包为了能够独立运行,简直是煞费苦心,将所有需要使用的资源全部都添加到了这个包里。这就是为什么这个jar包能独立运行的原因。
再来看之前的报错信息:
由于打包时没有使用那段配置,结果打包后形成了一个普通的jar包,在MANIFEST.MF文件中也就没有了Main-Class对应的属性了,所以运行时提示找不到主清单属性,这就是报错的原因。
上述内容搞清楚对我们编程意义并不大,但是对各位小伙伴理清楚SpringBoot工程独立运行的机制是有帮助的。其实整体过程主要是带着大家分析,如果以后遇到了类似的问题,多给自己提问,多问一个为什么,兴趣自己就可以独立解决问题了。
总结
- spring-boot-maven-plugin插件用于将当前程序打包成一个可以独立运行的程序包
往期专栏 |
---|
Java全栈开发 |
数据结构与算法 |
计算机组成原理 |
操作系统 |
数据库系统 |
物联网控制原理与技术 |