Spring Boot 提供了一个插件 spring-boot-maven-plugin 把程序打包成一个可执行的jar包,直接执行java -jar xxx.jar即可以启动程序
1、引用 spring-boot-maven-plugin插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2、打包文件内部结构
1)、BOOT-INF:
class:项目代码
lib:依赖jar包
2)、META-INF:程序入口
maven.xxx:程序配置文件
MANIFEST.MF
3)、org.springframework.boot.loader
3、MANIFEST.MF
Manifest-Version: 1.0
Implementation-Title: sk-cloud-order-sc
Implementation-Version: 1.0-SNAPSHOT
Start-Class: com.sk.order.sc.app
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 1.4.3.RELEASE
Created-By: Maven JAR Plugin 3.2.2
Main-Class: org.springframework.boot.loader.JarLauncher
Main-Class:org.springframework.boot.loader.JarLauncher:当我们使用java -jar xxx.jar启动jar包的时候通过调用JarLauncher#Main方法,不是我们定义的Main方法。SpringBoot内部提供一个可用于执行SpringBootApplication的工具类,这就是为什么打的包包含spring-boot-loader
4、JarLauncher源码查看
1)、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
<version>2.7.8</version>
</dependency>
2)、抽象Launcher类
用于启动应用程序,分别有JarLauncher、WarLauncher、PropertiesLauncher实现类
public abstract class Launcher {
private static final String JAR_MODE_LAUNCHER = "org.springframework.boot.loader.jarmode.JarModeLauncher";
public Launcher() {
}
protected void launch(String[] args) throws Exception {
if (!this.isExploded()) {
JarFile.registerUrlProtocolHandler();
}
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchivesIterator());
String jarMode = System.getProperty("jarmode");
String launchClass = jarMode != null && !jarMode.isEmpty() ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : this.getMainClass();
this.launch(args, launchClass, classLoader);
}
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
this.createMainMethodRunner(launchClass, args, classLoader).run();
}
//Archive相关代码
}
3)、Archive:归档文件获取Mainifest等URL路径(jar包中包含jar,或者jar包中class文件,那么会使用 !/ 分隔开)
public class JarFileArchive implements Archive {
public URL getUrl() throws MalformedURLException {
return this.url != null ? this.url : this.jarFile.getUrl();
}
public Manifest getManifest() throws IOException {
return this.jarFile.getManifest();
}
}
public class JarLauncher extends ExecutableArchiveLauncher {
static final Archive.EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");
};
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
protected boolean isPostProcessingClassPathArchives() {
return false;
}
protected boolean isNestedArchive(Archive.Entry entry) {
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
}
protected String getArchiveEntryPathPrefix() {
return "BOOT-INF/";
}
public static void main(String[] args) throws Exception {
(new JarLauncher()).launch(args);
}
}
Launcher#launch
protected void launch(String[] args) throws Exception {
if (!this.isExploded()) {
JarFile.registerUrlProtocolHandler();
}
//自定义类加载器加载jar文件(通过active的urls加载jar文件)
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchivesIterator());
String jarMode = System.getProperty("jarmode");
//获取manifest文件的Start-Class类
String launchClass = jarMode != null && !jarMode.isEmpty() ? "org.springframework.boot.loader.jarmode.JarModeLauncher" : this.getMainClass();
//调用Start-Class类类main方法
this.launch(args, launchClass, classLoader);
}
protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
this.createMainMethodRunner(launchClass, args, classLoader).run();
}
/*
* 调用manifest文件Start-Class类mian方法
*/
public class MainMethodRunner {
private final String mainClassName;
private final String[] args;
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = args != null ? (String[])args.clone() : null;
}
public void run() throws Exception {
Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.setAccessible(true);
mainMethod.invoke((Object)null, this.args);
}
}