spring-boot-maven-plugin插件打包和java -jar命令执行原理分析

 更新时间:2025年05月07日 09:18:36   作者:Armyyyyy丶  
这篇文章主要介绍了spring-boot-maven-plugin插件打包和java -jar命令执行原理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

1. Maven生命周期

Maven的生命周期有三种:

  1. clean:清除项目构建数据,较为简单,不深入探讨;
  2. site:建立和部署项目站点,使用的较少,也不深入探讨;
  3. default:定义了项目构建时所需要的所有步骤,是Maven生命周期中最核心最重要的的部分。

本次要深入了解的便是default流程。其生命周期如下:

阶段可否执行说明
validate验证项目是否正确以及所有必要信息是否可用
initializeX初始化构建状态
generate-sourcesX生成编译阶段需要的所有源码文件
process-sourcesX处理源码文件,例如过滤某些值
generate-resourcesX生成项目打包阶段需要的资源文件
process-resourcesX处理资源文件,并复制到输出目录,为打包阶段做准备
compile编译源代码,并移动到输出目录
process-classesX处理编译生成的字节码文件
generate-test-sourcesX生成编译阶段需要的测试源代码
process-test-sourcesX处理测试资源,并复制到测试输出目录
test-compileX编译测试源代码并移动到测试输出目录中
test使用适当的单元测试框架(如junit)运行测试
prepare-packageX在真正打包前执行一些必要的操作
package获取编译后的代码,并按照可发布的格式进行打包,如jar、war或ear文件
pre-integration-testX在集成测试执行之前,执行所需的操作,例如设置环境变量
integration-testX处理和部署所需的包到集成测试能够运行的环境中
post-integration-testX在集成测试被执行后执行必要的操作,例如清理环境
verify对集成测试的结果进行检查,以保证质量达标
install安装打包的项目到本地仓库,以供本地其它项目使用
deploy拷贝最终的包文件到远程仓库中,以共享给其它开发人员和项目

其中可以在Maven常见的Lifecycle中直接执行的有validate、compile、test、package、verify和deploy七种,一般在Maven的plugin标签中,可以通过配置如下配置来指定插件在某个阶段生效,需要注意的是不可随意配置,每个插件可处理的阶段都是不同的。(不配置则执行插件默认的)

<executions>
    <execution>
        <phase>XX</phase>
        <goals>
            <goal>XXXX</goal>
        </goals>
    </execution>
</executions>

今天要深入了解的spring-boot-maven-plugin插件就是在package阶段中生效的。

2. jar包结构

通常而言,jar包分为可执行jar包和不可执行jar包,顾名思义,可执行jar包即可通过命令java -jar直接执行,不可执行jar包通过命令java -jar执行则会报错。

2.1 不可执jar包结构

|-- _jar包根目录
    |-- 原项目class文件和resource文件
    |-- _META-INF
        |-- MANIFEST.MF
        |-- _maven
            |-- _项目目录
                |-- pom.properties
                |-- pom.xml

上面是经典的不可执行jar包目录,其中MANIFEST.MF文件内容如下:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: xxxxx
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_151

这五项是最基本的,如果使用java -jar执行这些jar包,将会抛出错误码java.launcher.jar.error3,意为没找到Main-Class属性。不同语言展示的最终描述不同,由launcher+对应语言类转换,简体中文在launcher_zh_CN类中转换,{0}为jar包名称,内容如下:

{0}中没有主清单属性

英文在launcher类中转换,{0}为jar包名称,内容如下:

no main manifest attribute, in {0}

2.2 可执行jar包结构

可执行jar包结构挑选经典的springboot启动包来做示范:

|-- _jar包根目录
    |-- _BOOT-INF
        |-- _classes
            |-- 原项目class文件和resource文件
        |-- _lib
            |--原项目依赖的jar库文件
    |-- _META-INF
        |-- MANIFEST.MF
        |-- spring-configuration-metadata.json(springboot项目特有)
        |-- build-info.properties
        |-- _maven
            |-- _项目目录
                |-- pom.properties
                |-- pom.xml

上一节我们得知了如果在MANIFEST.MF中没有Main-Class属性,使用java -jar命令执行jar包会报错,接下来看看在可执行jar包的结构,MANIFEST.MF中具体有什么属性:

Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: XXXX
Implementation-Version: 1.0-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Maven Archiver 3.4.0
Start-Class: XXX.XXX.XXX.XXXX
Main-Class: org.springframework.boot.loader.JarLauncher

里面有两个很重要的属性:Start-ClassMain-Class,其中Start-Class指的是项目中springboot的SpringApplication启动类,而Main-Class则是jar包的启动类入口。

3. spring-boot-maven-plugin插件打包

springboot打包插件执行原理:

  1. 读取原jar包:Maven插件都能读MavenProject对象内容,从中可以读取到Artifact信息,调用该对象的getFile()方法即可获取原jar包文件对象;
  2. 读取项目依赖jar库:直接使用MavenProject对象的getArtifacts()方法即可获取依赖的jar库;
  3. 加载launchScript:读取embeddedLaunchScript配置,并构建LaunchScript对象;
  4. 重新改写MANIFEST.MF:到此步骤开始为repackage的核心流程,改写清单文件时最主要的便是写入Start-ClassMain-Class属性,除此之外还会写入jar库和原项目文件目录属性;
  5. 写入spring-boot-loader包文件:该包是springboot对接java -jar执行命令的核心处理逻辑,springboot打包后加入的Main-Class: org.springframework.boot.loader.JarLauncher属性指向的类便是此包中的jar包启动类,如果war包则会写入war包启动类;
  6. 写入原项目文件:原项目文件会被挪到BOOT-INF/classes/目录下;
  7. 写入项目依赖jar库:原项目依赖的jar库会被写入到BOOT-INF/lib/目录下。

如果要看spring-boot-maven-plugin插件打包源码以分析原理,可导入插件的依赖,此时就能看到该插件的源码。

如果使用的是IDEA,下载源码后打上断点,在执行package时,使用debug模式启动也能直接进行调试。

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

4. 执行jar原理

将会分析执行java -jar命令后,Java程序调用到Springboot启动类main方法的流程。

  1. JVM启动,执行加载主函数LoadMainClass:此时是在JVM底层实现的,里面指定了LauncherHelper类;
  2. 执行LauncherHelpercheckAndLoadMain方法:JVM将会调用LauncherHelpercheckAndLoadMain方法,解析并校验jar包,并获取主要的启动类;
  3. 解析jar的MANIFEST.MF文件:在此方法中会完成读取MANIFEST.MF文件,主要是读取其中的Main-Class属性,并做jar包启动的校验;
  4. GetStaticMethodID方法:JVM获取到Main-Class类对象,调用Main-Class类对象的main方法;
  5. 执行JarLauncher的main方法:JarLauncher继承自Launcher,main方法最后还是会调用到Launcher.launch()方法中;
  6. 读取jar的Start-Class:此时会读取jar包的Start-Class属性,该属性就是原项目的SpringApplication启动类;
  7. 调用启动类的main方法:调用MainMethodRunner的run方法,里面会调用Start-Class类的main方法
  8. 此时调入到自定义的启动类中,完成启动Springboot程序的入口程序。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 深入了解Java数据结构和算法之堆

    深入了解Java数据结构和算法之堆

    这篇文章主要为大家介绍了Java数据结构和算法之堆 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • Java实现控制台输出两点间距离

    Java实现控制台输出两点间距离

    这篇文章主要介绍了Java实现控制台输出两点间距离,涉及了部分编程坐标的问题,具有一定参考价值,需要的朋友可以了解下
    2017-09-09
  • JAVA动态维度笛卡尔积输出的实现

    JAVA动态维度笛卡尔积输出的实现

    本文主要介绍了JAVA动态维度笛卡尔积输出的实现,通过动态生成笛卡尔积,可以方便地处理多维数据集,提高数据处理效率,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 解析SpringBoot中使用LoadTimeWeaving技术实现AOP功能

    解析SpringBoot中使用LoadTimeWeaving技术实现AOP功能

    这篇文章主要介绍了SpringBoot中使用LoadTimeWeaving技术实现AOP功能,AOP面向切面编程,通过为目标类织入切面的方式,实现对目标类功能的增强,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • 基于@PathVariable注解的用法说明

    基于@PathVariable注解的用法说明

    这篇文章主要介绍了基于@PathVariable注解的用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • SpringBoot中的HATEOAS详情

    SpringBoot中的HATEOAS详情

    这篇文章主要介绍了SpringBoot中的HATEOAS详情,SpringBoot提供了HATEOAS的便捷使用方式,文章围绕主题展开详细介绍内容,需要的小伙伴可以参考一下
    2022-05-05
  • SpringBoot项目POM文件的使用小结

    SpringBoot项目POM文件的使用小结

    本文主要详细介绍了Maven中SpringBoot项目的POM文件配置,包括项目的依赖和插件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • 在Ubuntu系统下安装JDK和Tomcat的教程

    在Ubuntu系统下安装JDK和Tomcat的教程

    这篇文章主要介绍了在Ubuntu系统下安装JDK和Tomcat的教程,这样便是在Linux系统下搭建完整的Java和JSP开发环境,需要的朋友可以参考下
    2015-08-08
  • Java常用工具类总结

    Java常用工具类总结

    今天带大家学习Java常用工具类,文中有非常详细的图文解说及代码示例,对正在学习java的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • 深入了解Java排序算法

    深入了解Java排序算法

    本文主要介绍了深入了解Java排序算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2007-03-03

最新评论