前言
通過java -jar xxx.jar
這個啟動java程序的套路是Jar包本來就有的特性,sb只是利用了這個強(qiáng)大的功能簡化了應(yīng)用的啟動。
MANIFEST.MF
MANIFEST.MF在jar包的META-INF文件夾下,通過jar包啟動應(yīng)用的配置就在這個文件里面。下面看下我的一個sb應(yīng)用的該文件內(nèi)容
Manifest-Version: 1.0
Implementation-Title: demo
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: com.example.demo.DemoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.5.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
MANIFEST.MF中配置分成兩部分,自定義屬性和非自定義屬性。
非自定義屬性就是MANIFEST.MF這個文件中規(guī)定的一些特定屬性束倍,上面的Manifest-Version,Implementation-Title,Implementation-Version绪妹,Created-By甥桂,Main-Class都是
而自定義屬性,就是自己隨便取名的屬性邮旷,比如Start-Class黄选,Spring-Boot-Classes,Spring-Boot-Lib
其中非自定義屬性Main-Class十分重要婶肩,java -jar xxx.jar
就是從這個屬性對應(yīng)class的main作為入口啟動應(yīng)用的办陷。
但是我們使用maven插件打包的時候配置的main-class其實(shí)對應(yīng)的Start-Class這個配置,為什么要這樣呢律歼?因?yàn)橐粋€應(yīng)用啟動還需要用類加載器加載一些其他資源比如jar包以及應(yīng)用自己的class類民镜,所以Main-Class就做了這些事情。
sb jar包內(nèi)容一覽
--META-INF
----MANIFEST.MF
----maven
------com.example
--------demo
----------pom.xml
----------pom.properties
--listfile.py
--test.jar
--BOOT-INF
----classes
------com
--------example
----------demo
------------DemoApplication.class
------application.properties
----lib
------tomcat-embed-websocket-9.0.19.jar
------jackson-databind-2.9.8.jar
------jackson-core-2.9.8.jar
...
--org
----springframework
------boot
--------loader
----------archive
----------JarLauncher.class
...
通過java -cvf xx.jar
解壓jar包后會得到以上格式的文件目錄险毁,分為三個文件夾
文件夾 | 作用 |
---|---|
META-INF | 一些配置文件制圈,MANIFEST.MF就在這里 |
BOOT-INF | 子文件class存儲應(yīng)用類,lib存儲第三方j(luò)ar包 |
org | main-class對應(yīng)的JarLauncher在這里畔况,這邊的類用于sb應(yīng)用啟動 |
一些源碼
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);
}
protected boolean isNestedArchive(Entry entry) {
return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/");
}
public static void main(String[] args) throws Exception {
(new JarLauncher()).launch(args);
}
}
jar包通過JarLauncher的main
方法啟動鲸鹦,我們看到這里就一行代碼
(new JarLauncher()).launch(args);
這行代碼對應(yīng)2個邏輯
- 初始化包換三方j(luò)ar包和當(dāng)前應(yīng)用的class文件的類加載器
- 啟動我們配置的SpringApplication
new JarLauncher()負(fù)責(zé)加載我們當(dāng)前這個jar包,launch負(fù)責(zé)執(zhí)行以上兩個邏輯跷跪。
JarLauncher默認(rèn)構(gòu)造函數(shù)實(shí)現(xiàn)為空亥鬓,它父類ExecutableArchiveLauncher會調(diào)用再上一級父類Launcher的createArchive方法加載jar包
public JarLauncher() {
}
public ExecutableArchiveLauncher() {
try {
this.archive = this.createArchive();
} catch (Exception var2) {
throw new IllegalStateException(var2);
}
}
protected final Archive createArchive() throws Exception {
ProtectionDomain protectionDomain = this.getClass().getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URI location = codeSource != null ? codeSource.getLocation().toURI() : null;
String path = location != null ? location.getSchemeSpecificPart() : null;
if (path == null) {
throw new IllegalStateException("Unable to determine code source archive");
} else {
File root = new File(path);
if (!root.exists()) {
throw new IllegalStateException("Unable to determine code source archive from " + root);
} else {
return (Archive)(root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));
}
}
}
加載了jar包之后,我們就能獲得它里面所有的資源域庇,也就是上一節(jié)列出的那些文件。
launch的實(shí)現(xiàn)在父類Launcher中
//Launcher
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
this.launch(args, this.getMainClass(), classLoader);
}
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
this.createMainMethodRunner(mainClass, args, classLoader).run();
}
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
return new MainMethodRunner(mainClass, args);
}
//MainMethodRunner
public void run() throws Exception {
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke((Object)null, this.args);
}
不多做解釋覆积,上面的代碼很清晰的展示了launch方法的邏輯听皿,通過createClassLoader構(gòu)造類加載器,通過this.createMainMethodRunner(mainClass, args, classLoader).run()啟動我們的sb應(yīng)用宽档。