Spring-Boot 可执行jar包或war包的启动源码分析

在增加完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 来执行。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值