在增加完maven插件并在packaging为 jar 的情况下
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
运行 mvn package,发现jar包unzip后发现文件格式如下:
-app-by-gui/target/temp
╰─➤ ls
BOOT-INF META-INF org
╭─zhou@zhoudembp ~/think_in_boot_workspace/thinking-in-spring-boot-samples/first-app-by-gui/target/temp
╰─➤ ls
BOOT-INF META-INF org
╭─zhou@zhoudembp ~/think_in_boot_workspace/thinking-in-spring-boot-samples/first-app-by-gui/target/temp
╰─➤ ls BOOT-INF
classes lib
╭─zhou@zhoudembp ~/think_in_boot_workspace/thinking-in-spring-boot-samples/first-app-by-gui/target/temp
╰─➤ ls META-INF
MANIFEST.MF maven
╭─zhou@zhoudembp ~/think_in_boot_workspace/thinking-in-spring-boot-samples/first-app-by-gui/target/temp
╰─➤ ls org
springframework
BOOT_INF:包含了项目中的编译后class及第三方lib;如果是war包的话,此处是WEB-INF目录
META-INF:和一般的可执行jar包一样包含manifest主文件及maven pom
lib:包含spring-boot的启动相关class
manifeist.mf文件内容如下,可以看到整体的启动配置Main-Class 及 Start-Class:
Manifest-Version: 1.0
Implementation-Title: first-app-by-gui
Implementation-Version: 0.0.1-SNAPSHOT
Built-By: zhou
Implementation-Vendor-Id: thinking-in-spring-boot
Spring-Boot-Version: 2.0.2.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: thinkinginspringboot.firstappbygui.FirstAppByGuiApplicati
on
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.6.1
Build-Jdk: 1.8.0_211
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo
ot-starter-parent/first-app-by-gui
可以发现 Main-Class 就是 springboot 自己的启动类,即是上文中org目录下的class。
由于打的是jar包,所以使用的是 JarLauncher;换句话说,如果packaging中打的是war包的话,生成的Mainfest文件中的Main-Class就会是WarLauncher;具体源码这里不概述,简要说一下:
1、通过Main-Class启动SpringBoot的主程序;
2、根据jar或者war 的不同启动器,JarLauncher中写死从BOOT-INF中加载jar与class,WarLauncher写死的是WEB-INF
3、代码中会生成classLoader,并设置一些基础环境值
4、这时候manifest文件中的Start-Class就起到作用了,一般的manifest中是不包含这个属性的。代码中会利用java的反射执行startclass的main方法,源码如下:
//JarLauncher类:
//main函数:
public static void main(String[] args) throws Exception {
(new JarLauncher()).launch(args);
}
//Launcher类:
//第一步:
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
this.launch(args, this.getMainClass(), classLoader);
}
//ExecutableArchiveLauncher类:
//获取mainClass:
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
mainClass = manifest.getMainAttributes().getValue("Start-Class");
}
if (mainClass == null) {
throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
} else {
return mainClass;
}
}
//第二步:
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
//这里就是根据找到的startclass通过反射调用main方法了,通过创建mainMethodRunner对象反射调用自己项目中的springboot启动类,即下面的 FirstAppByGuiApplication 类 的main函数
this.createMainMethodRunner(mainClass, args, classLoader).run();
}
//MainMethodRunner类:
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 = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke((Object)null, this.args);
}
}
// 上面可以很明显的看出是反射调用
项目入口:
@SpringBootApplication
public class FirstAppByGuiApplication {
public static void main(String[] args) {
SpringApplication.run(FirstAppByGuiApplication.class, args);
}
}
5、通过这种方式生成的无论是jar或是war包,都是内嵌tomcat的,都可以通过java -jar xxxx.jar 或 java -jar xxx.war 来执行。