spring-boot-maven-plugin
我們直接使用 maven package
(maven自帶的package打包功能),打包Jar包的時候,不會將該項目所依賴的Jar包一起打進去,在使用java -jar
命令啟動項目時會報錯,項目無法正常啟動。這個時候,我們就可以考慮引用spring-boot-maven-plugin
插件來為項目打Jar包。
maven項目的pom.xml中,添加了下述插件,當(dāng)運行 maven package 進行打包時,會打包成一個可以直接運行的JAR(fat jar)文件,使用 java -jar 命令就可以直接運行。
注意:如果你的項目沒有繼承spring-boot-starter-parent 這個POM,你需要做如下配置,將目標(biāo)綁定到repackage。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
默認(rèn)情況下maven項目的打包命令,在打Jar包時不會把依賴的jar包也打包進去,但是spring-boot-maven-plugin插件,會將依賴的jar包全部打包進去。例如下面這個使用spring-boot-maven-plugin插件打包生成的jar包的BOOT/INF/lib
目錄下面就包含了所有依賴的jar包:
引入了spring-boot-maven-plugin插件后,在使用打包功能時會將mvn package 生成的 jar或者war 重新打包成可執(zhí)行文件,同時修改原文件名,增加.origin 后綴。
可執(zhí)行 Jar 包內(nèi)部結(jié)構(gòu)
將打出來的可執(zhí)行 Jar 解壓開我們能看到下面的結(jié)構(gòu):
可執(zhí)行 jar 目錄結(jié)構(gòu)
├─BOOT-INF
│ ├─classes
│ └─lib
├─META-INF
│ ├─maven
│ ├─app.properties
│ ├─MANIFEST.MF
└─org
└─springframework
└─boot
└─loader
├─archive
├─data
├─jar
└─util
結(jié)構(gòu)對比
從上面的文件結(jié)構(gòu)和 jar 清單內(nèi)容來看,Spring Boot 打包后的 fatjar 對比 源 jar 主要有以下差異:
- 源 jar 中主項目的
.class
文件被移至fatjar
的BOOT-INF/classes
文件夾下。 - 新增
BOOT-INF/lib
文件夾,里面存放三方 jar 文件。 - 新增
BOOT-INF/classpath.idx
,用來記錄classpath
的加載順序。 - 新增
org/springframework/boot/loader
文件夾,這是spring-boot-loader
編譯后的 .class 文件。 - 清單文件
MANIFEST.MF
中新增以下屬性: -
Spring-Boot-Classpath-Index
: 記錄classpath.idx
文件的地址。 -
Start-Class
: 指定Spring Boot
的啟動類。 -
Spring-Boot-Classes
: 記錄主項目的.class
文件存放路徑。 -
Spring-Boot-Lib
: 記錄三方 jar 文件存放路徑。 -
Spring-Boot-Version
: 記錄Spring Boot
版本信息 -
Main-Class
: 指定 jar 程序的入口類(可執(zhí)行 jar 為org.springframework.boot.loader.JarLauncher
類)。
我們先來重點關(guān)注兩個地方:META-INF 下面的 Jar 包描述文件和 BOOT-INF 這個目錄。
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: xxxx
Start-Class: com.xxxx.AppServer
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_73
Main-Class: org.springframework.boot.loader.JarLauncher
在上面我們看到一個熟悉的配置Main-Class: org.springframework.boot.loader.JarLauncher。我們大概能猜到這個類是整個系統(tǒng)的入口。
再看下 BOOT-INF 這個目錄下面,我們會發(fā)現(xiàn)里面是我們項目打出來的 class 文件和項目依賴的 Jar 包??吹竭@里,你可能已經(jīng)猜到 Spring Boot 是怎么啟動項目的了。
Spring Boot 是怎么啟動項目
JarLauncher
public class JarLauncher extends ExecutableArchiveLauncher {
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
if (entry.isDirectory()) {
return entry.getName().equals(BOOT_INF_CLASSES);
}
return entry.getName().startsWith(BOOT_INF_LIB);
}
public static void main(String[] args) throws Exception {
//項目入口,重點在launch這個方法中
new JarLauncher().launch(args);
}
}
//launch方法
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
//創(chuàng)建LaunchedURLClassLoader。如果根類加載器和擴展類加載器沒有加載到某個類的話,就會通過LaunchedURLClassLoader這個加載器來加載類。這個加載器會從Boot-INF下面的class目錄和lib目錄下加載類。
ClassLoader classLoader = createClassLoader(getClassPathArchives());
//這個方法會讀取jar描述文件中的Start-Class屬性,然后通過反射調(diào)用到這個類的main方法。
launch(args, getMainClass(), classLoader);
}
簡單總結(jié)
-
Spring Boot 可執(zhí)行 Jar 包的入口點是 JarLauncher 的 main 方法;
-
這個方法的執(zhí)行邏輯是先創(chuàng)建一個 LaunchedURLClassLoader,這個加載器加載類的邏輯是:
先判斷根類加載器和擴展類加載器能否加載到某個類,如果都加載不到就從 Boot-INF 下面的 class 和 lib 目錄下去加載;
-
讀取Start-Class屬性,通過反射機制調(diào)用啟動類的 main 方法,這樣就順利調(diào)用到我們開發(fā)的 Spring Boot 主啟動類的 main 方法了。
Goal
spring-boot-maven-plugin 官方文檔 介紹的有五種 goal,分別如下:
- spring-boot:build-image: 將程序使用 buildpack 打包進容器鏡像中。
- spring-boot:build-info: 根據(jù)當(dāng)前 MavenProject 的內(nèi)容生成一個 build-info.properties 文件
- spring-boot:help: 顯示幫助信息。調(diào)用mvn spring-boot:help -Ddetail=true -Dgoal=以顯示參數(shù)詳細(xì)信息。
- spring-boot:repackage: 默認(rèn)的 goal,將普通 mvn package 打包成的 jar 重新打包成包含所有程序依賴項的可執(zhí)行 jar/war 文件,并保留 mvn package 打包的 jar 為 .original 后綴
- spring-boot:run: 運行 Spring Boot 應(yīng)用。
- spring-boot:start: 通常用于集成測試方案中,在 mvn integration-test 階段管理 Spring Boot 應(yīng)用的生命周期。
- spring-boot:stop: 停止已通過 start 目標(biāo)啟動的應(yīng)用程序。通常在 integration-test 完成后調(diào)用。
文章只對 repackage 進行分析,我們先直接看下 spring-boot-maven-plugin 的源代碼結(jié)構(gòu):
spring-boot-maven-plugin:打包時排除provided依賴
spring-boot-maven-plugin 插件提供spring boot的maven打包支持。項目中scope為provided的依賴,比如 lombok、mybatis-plus 等,只作用于編譯階段,編譯完成就可以功成身退了。在spring maven打包時,provided依賴會排除在包外,但springboot maven打包時,還會將這些依賴打進 war 包的 lib-provided 文件夾里或 jar 包的 lib 文件夾里。
springboot項目構(gòu)建jar包或war包的命令為repackage,作用于maven生命周期的package階段,在 mvn package 執(zhí)行之后,這個命令再次打包生成可執(zhí)行的包,例如打jar包時,生成可執(zhí)行jar包,同時將 mvn package 生成的 jar 重命名為 *.origin。默認(rèn)情況下,repackage會將工程中引入的任何依賴打到包中。
以lombok為例,官方提供了以下方式可以將provided依賴從springboot項目包中排除。
1. 通過指定groupId和artifactId排除某個特定的依賴
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2. 指定groupId排除groupId相關(guān)的所有依賴
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeGroupIds>org.projectlombok</excludeGroupIds>
</configuration>
</plugin>
</plugins>
</build>
2. 按照配置文件排除依賴
<profiles>
<profile>
<id>dev</id>
<activation>
<property>
<name>environment</name>
<value>dev</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.12</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>prod</id>
<activation>
<property>
<name>environment</name>
<value>prod</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.12</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
mvn clean package -Pdev
使用dev配置文件去除lombok 依賴
SpringBoot 工程部署的 jar 包瘦身
通過上方分析 fat jar下 有 classes,lib 兩個文件夾,我們編譯好的代碼是放在 classes 里面的,而我們所依賴的 jar 包都是放在 lib 文件夾下
classes 部分是非常小的(我的是160kb左右),lib部分是非常大的(我的是70M左右),所以上傳很慢
那我們可以將我們自己寫的代碼部分與所依賴的 maven jar 包部分拆開上傳,每次只需要上傳我們自己寫的代碼部分即可
正常打包
首先,我們項目的 pom.xml 文件中的打包方式如下:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
這是 SpringBoot 中默認(rèn)的打包方式,我們先按照這種方式打包出來,得到一個 jar 包,我們將 jar 包解壓,如果不能直接解壓,則將后綴改為 zip 再進行解壓,我們只需要拿到 BOOT-INF
中的 lib 目錄即可
改變打包方式
我們對 SpringBoot 中默認(rèn)的打包方式做一些配置
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.Application</mainClass>
<layout>ZIP</layout>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
-
mainClass
,我們指定了項目的啟動類 -
layout
,我們指定了打包方式為 ZIP,注意:一定是大寫的 -
includes
,有自己的依賴 jar,可以在此導(dǎo)入 -
repackage
,剔除其它的依賴,只需要保留最簡單的結(jié)構(gòu)
再次打包
我們再次點擊 maven package
,得到一個 jar 包,可以看到此時的 jar 包只有167kb了(原先70M+)
上傳啟動
我們將 lib 目錄,以及最后打包的瘦身項目 jar 包,上傳至服務(wù)器同一目錄
使用命令文章來源:http://www.zghlxwxcb.cn/news/detail-462113.html
nohup java -Dloader.path=./lib -jar ./sbm-0.0.1-SNAPSHOT.jar &
-
-Dloader.path
,告訴它所依賴的 maven jar 包位置 -
admin4j-example-1.0.jar
,項目 jar 包的名字 -
nohup、&
,使得 jar 包在服務(wù)后臺運行
總結(jié)
使用瘦身部署,方便每次的迭代更新,不用每次都上傳一個很大的 jar 包,從而節(jié)省部署時間。但是每次依賴jar包版本或者有新增了,需要更改下lib目錄數(shù)據(jù)文章來源地址http://www.zghlxwxcb.cn/news/detail-462113.html
到了這里,關(guān)于SpringBoot 插件 spring-boot-maven-plugin 原理,以及SpringBoo工程部署的 jar 包瘦身實戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!