深入透析springboot加载之spring-boot-maven-plugin,应该是目前最详细的讲解了

如下图,从springboot官网下载一个demo,执行mvn install 命令 即可生成一个可以执行的springboot的jar包。在这里插入图片描述

上面怎么有两个文件?首先我们查看springboot项目的pom文件有以下引入,这两个文件是由spring-boot-maven-plugin生成的。

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

经过spring-boot-maven-plugin插件加工,original后缀的文件原本是个jar后缀的文件,被重命名为original后缀文件了(后文简称源jar)。而现在看到的jar后缀是由源jar文件经过spring-boot-maven-plugin生成的一个fatjar文件(后文简称springboot fatjar)。什么是fatjar? fatjar就是将一个jar及其依赖的三方jar全部打到一个包中。因为springboot fatjar里面额外包含其依赖的三方jar包,所以比源jar大很多,如第一个图文件一个8m一个6kb。

解压源jar和spring的fatjar的文件,使用tree命令得到以下目录结构

源jar的结构

├── META-INF
│   ├── MANIFEST.MF 
│   └── maven
│       └── com.jianshu.springboot.test
│           └── com.jianshu.springboot.test.loader
│               ├── pom.properties
│               └── pom.xml
├── application.properties
└── Application.class

spring fatjar文件结构

├── BOOT-INF
│   ├── classes //加载主工程编译的class(spingboot jar特有的)
│       ├── application.properties
│       └── Application.class
│   └── lib // 这里存放的是应用的Maven依赖的jar包文件 这下面存放的是Spring boot loader的.class文件
│       ├── afirst.spring.boot.jar.test-1.0-SNAPSHOT.jar
│       └── ...
├── META-INF // 这里和源文件一样,共有的文件夹,存放是jar和启动信息,如main class
│   ├── MANIFEST.MF 清单文件 jar最重要的文件,程序启动的入口
│   └── maven
│       └── com.jianshu.springboot.test
│           └── com.jianshu.springboot.test.loader
│               ├── pom.properties
│               └── pom.xml
└── org
    └── springframework
        └── boot
            └── loader //这里存放的是Spring boot loader的.class文件
                ├── ExecutableArchiveLauncher.class
                ├── JarLauncher.class
                ├── ....

现在是探索spring-boot-maven-plugin如何通过第一个jar结构生成第二个。对此文件结构在此不做分析。我需要们分析spingboot的spring-boot-maven-plugin源码。 “spring-boot-maven-plugin”,见名知义,这个是一个maven的构建插件。通过阅读这个源码我们也可以简单了解一下maven的插件知识。不过为了阅读这个源码,博主特定去了解以下maven插件方面的知识,建议大家也去了解以下。
阅读源码必要手段个人觉得要提前准备调试的环境,就是有个项目去运行这个东东,方便调试。 如下是博主创建的test项目,专门来测试插件。
在这里插入图片描述
由上图我们可以看到,Spring Boot Maven plugin提供5种操作,maven标准说法是5种goal。

  1. spring-boot:repackage,默认goal。在mvn package之后,再次打包可执行的jar/war,同时保留mvn package生成的jar/war为.origin
  2. spring-boot:run,运行Spring Boot应用
  3. spring-boot:start,在mvn integration-test阶段,进行Spring Boot应用生命周期的管理
  4. spring-boot:stop,在mvn integration-test阶段,进行Spring Boot应用生命周期的管理
  5. spring-boot:build-info,生成Actuator使用的构建信息文件build-info.properties
    文章只针对repackage进行分析,大家感兴趣可以自行去看其他的操作。见名知义repackage负责的工作这个对之前的maven构建的jar进行了重新打包。下面是博主打开的spring-boot-maven-plugin项目的源码目录文件结构
    在这里插入图片描述
    先入眼帘就是 RepackageMojo这个类。这个就是repackage操作的入口类了。先别急 打开阅读源码 ,我们先来看看这个类的继承关系。
    在这里插入图片描述
    由类图继承关系可以知道,这个类实现了Mojo接口,还有Mojo注解。什么是Mojo?在这里简单给大家普及官方解释,Mojo 就是Maven plain Old Java Object。每一个 Mojo 就是 Maven 中的一个执行目标(executable goal),而插件则是对单个或多个相关的 Mojo 做统一分发。
    简单说,你写maven插件,mojo这个东西就是入口。RepackageMojo类实现了mojo接口就相当于实现maven的执行入口。我们打开mojo这个接口。
    在这里插入图片描述
    这个接口包含了execute这个方法,这个方法会在maven执行构建goal的时候回调执行程序想要的操作。所以实现逻辑都在这个方法里面。
    现在就可以打开RepackageMojo这个类探探究竟
    在这里插入图片描述
    第一眼又看到repackage这个关键字,这里对应就是我前面项目所看到的repackage操作,对于注解我再次不做多说明,文章对于插件编写不做过多分析,大家有兴趣可以去官网看看教程。好的我们跳到这个类的execute方法的实现。

    由上图看到repackage才是真正执行打包操作的方法,如下图即是repackage方法
    在这里插入图片描述
    由上图可知,该方法具体步骤。
    1.获取源文件的Artifact
    2.创建并获取打包文件,这里仅仅是生成,并没有写入任何东西
    3.通过源文件构建一个打包对象Repackager。
    4.获取项目的所有依赖Artifact,并且过滤一些不需要依赖对象
    5.构建依赖对象
    6.获取启动脚本
    7.由步骤3构建的打包对象进行重新打包
    8.更新Artifact
    其中1,2,3步骤,注释上面已经写得比较通俗。 我们发现第4个步骤返回的依赖是一个set对象,set可以去重,但是set有可能是无序的。但是经过几次打包测试发现,最终打出来的包里面的依赖jar都是有序的且跟pom的声明顺序有关。打开此方法看看
    在这里插入图片描述
    返回的是LinkHashSet,它是一个有序的set,所以打出来的包是有序的,正好验证了之前猜想。(就是博主没打开源码之前反推肯定是个有序的set)
    至于5到6步骤,跟打包关系不大,直接跳过。接下来我进去看看第7步骤的代码,这才是生成打包文件的核心代码。
    在这里插入图片描述
    在这里插入图片描述
    上面源码可知,第4步骤可以知道为啥会出现一个original后缀的文件了。
    在这里插入图片描述
    那么如下文件结构是怎么生成的呢?带着疑问继续
    源jar的结构
├── META-INF
│   ├── MANIFEST.MF 
│   └── maven
│       └── com.jianshu.springboot.test
│           └── com.jianshu.springboot.test.loader
│               ├── pom.properties
│               └── pom.xml
├── application.properties
└── Application.class

spring fatjar文件结构

├── BOOT-INF
│   ├── classes //加载主工程编译的class(spingboot jar特有的)
│       ├── application.properties
│       └── Application.class
│   └── lib // 这里存放的是应用的Maven依赖的jar包文件 这下面存放的是Spring boot loader的.class文件
│       ├── afirst.spring.boot.jar.test-1.0-SNAPSHOT.jar
│       └── ...
├── META-INF // 这里和源文件一样,共有的文件夹,存放是jar和启动信息,如main class
│   ├── MANIFEST.MF 清单文件 jar最重要的文件,程序启动的入口
│   └── maven
│       └── com.jianshu.springboot.test
│           └── com.jianshu.springboot.test.loader
│               ├── pom.properties
│               └── pom.xml
└── org
    └── springframework
        └── boot
            └── loader //这里存放的是Spring boot loader的.class文件
                ├── ExecutableArchiveLauncher.class
                ├── JarLauncher.class
                ├── ....

我们继续打开们打开第7步骤的的repackage(jarFileSource, destination, libraries, launchScript);方法
在这里插入图片描述

由上图可知,repackage方法的第2第3步骤分别生成MANIFEST.MF文件和org.spingframework.boot.loader目录下的文件,但是 BOOT-INF/classes 和 BOOT-INF/lib文件怎么来?分析以下步骤5就知道了,由源码可知我需要继续打开RepackagingLayout看看究竟
在这里插入图片描述
打开RepackagingLayout继承的Layout接口
在这里插入图片描述
那么RepackagingLayout实现类在那里,我们原先这个类,看看步骤2,就是在这里被初始化的
在这里插入图片描述
我们打org.springframework.boot.loader.tools.Repackager#getLayoutFactory这个方法
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
跳转半天,这里Jar是最终的实现的RepackagingLayout这个接口
在这里插入图片描述
上面的类分析,这个jar实现RepackagingLayout这个接口,我们来分析上图的类。根据注释可以知道RepackagingLayout这个接口 ,一种特殊的布局,通过移动现有的存档文件来重新打包内容到一个新位置,毫无疑问他决定了新打包jar的文件结构。由Jar这class可以直观看到到启动的类org.springframework.boot.loader.JarLauncher,迁移文件依赖包路径是BOOT-INF/lib/,迁移class的位置是BOOT-INF/classes/。全部验证了文章首页看到的jar的结构,如下

├── BOOT-INF
│   ├── classes //加载主工程编译的class(spingboot jar特有的)
│       ├── application.properties
│       └── Application.class
│   └── lib // 这里存放的是应用的Maven依赖的jar包文件 这下面存放的是Spring boot loader的.class文件
│       ├── afirst.spring.boot.jar.test-1.0-SNAPSHOT.jar
│       └── ...
├── META-INF // 这里和源文件一样,共有的文件夹,存放是jar和启动信息,如main class
│   ├── MANIFEST.MF 清单文件 jar最重要的文件,程序启动的入口
│   └── maven
│       └── com.jianshu.springboot.test
│           └── com.jianshu.springboot.test.loader
│               ├── pom.properties
│               └── pom.xml
└── org
    └── springframework
        └── boot
            └── loader //这里存放的是Spring boot loader的.class文件
                ├── ExecutableArchiveLauncher.class
                ├── JarLauncher.class
                ├── ....

通过阅读源码,文章至此已经分析完spring-boot-maven-plugin如何通过一个jar文件重新打包成fatjar文件。还有第二点,spring-boot-maven-plugin 生成fatjar里面的依赖jar总是和pom文件声明的顺序一样,这点涉及到springboot的加载类启动原理,知道这个原理可以排查线上很多问题,博主不在此做过多声明,后续文章会讲解这个。因为这是博主第一次写博客,由任何错误和或者不足的地方请大家多多谅解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值