打可執行 Jar 包
將 Spring Boot 應用打成可執行 Ja r包很容易,只需要在 pom 中加上一個 Spring Boot 提供的插件,然後在執行mvn package即可。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
注意:
如果你的項目沒有繼承spring-boot-starter-parent這個POM,你需要做如下配置,將目標綁定到repackage。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
關於這個插件的詳細使用,可以參考官網
運行完mvn package後,我們會在 target 目錄下看到兩個 jar 文件。myproject-0.0.1-SNAPSHOT.jar 和 myproject-0.0.1-SNAPSHOT.jar.original。第一個 jar 文件就是我們應用的可執行 jar 包,第二個 Jar 文件是應用原始的 jar 包。
可執行 Jar 包內部結構
將打出來的可執行 Jar 解壓開我們能看到下面的結構:
可執行 jar 目錄結構
├─BOOT-INF
│ ├─classes
│ └─lib
├─META-INF
│ ├─maven
│ ├─app.properties
│ ├─MANIFEST.MF
└─org
└─springframework
└─boot
└─loader
├─archive
├─data
├─jar
└─util
我們先來重點關注兩個地方: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。我們大概能猜到這個類是整個系統的入口。
再看下 BOOT-INF 這個目錄下面,我們會發現裏面是我們項目打出來的 class 文件和項目依賴的 Jar 包。看到這裏,你可能已經猜到 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();
//創建LaunchedURLClassLoader。如果根類加載器和擴展類加載器沒有加載到某個類的話,就會通過LaunchedURLClassLoader這個加載器來加載類。這個加載器會從Boot-INF下面的class目錄和lib目錄下加載類。
ClassLoader classLoader = createClassLoader(getClassPathArchives());
//這個方法會讀取jar描述文件中的Start-Class屬性,然後通過反射調用到這個類的main方法。
launch(args, getMainClass(), classLoader);
}
簡單總結
- Spring Boot 可執行 Jar 包的入口點是 JarLauncher 的 main 方法;
- 這個方法的執行邏輯是先創建一個 LaunchedURLClassLoader,這個加載器加載類的邏輯是:先判斷根類加載器和擴展類加載器能否加載到某個類,如果都加載不到就從 Boot-INF 下面的 class 和 lib 目錄下去加載;
- 讀取Start-Class屬性,通過反射機制調用啓動類的 main 方法,這樣就順利調用到我們開發的 Spring Boot 主啓動類的 main 方法了。
遠程調試
這邊介紹個調試 JarLauncher 啓動的小技巧:IDEA 遠程調試。
step1:我們以下面的方式啓動我們的 Spring Boot 應用
java -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y -jar App.jar
step2:在 idea 中進行如下配置
然後點擊下面的執行按鈕執行即可