快速掌握SpringBoot应用的启动入口

 更新时间:2022年05月23日 09:41:29   作者:Chub  
本篇并不是深究内置服务器的启动过程,而是追溯Springboot启动之前到底做了什么?它是如何与我们经常写的@SpringBootApplication注解注释的main方法类绑定起来的?对SpringBoot启动入口相关知识感兴趣的朋友一起看看吧

Springboot可以说是Java程序员必备技能了,大家都知道Springboot最终可以通过maven打成jar包,然后直接使用java -jar命令运行一个Web工程(或其它)。这样就避免了原先基于tomcat的web工程的复杂操作。Springboot能够使Web服务的部署简单到如此程度是因为其内置了Jetty(或Tomcat)服务器,并且在容器启动过程中start该服务器,成功运行Web服务。

本篇并不是深究内置服务器的启动过程,而是追溯Springboot启动之前到底做了什么?它是如何与我们经常写的@SpringBootApplication注解注释的main方法类绑定起来的?

1、一切的开始

相信各位Springbooter一定不会陌生下面的代码,无论是初学Springboot的新同学,或是开始研究Springboot源码的新司机,这段代码几乎是我们的落脚点。我们如此熟悉它,以至于认为它就是Springboot这个魔法乐园的起点。但真的是这样吗?

 @SpringBootApplication
 public class Springboot01helloworldApplication {
     public static void main(String[] args) {
         SpringApplication.run(Springboot01helloworldApplication.class, args);
     }
 }

我们都知道,一个Java工程打包过后,这个jar包的入口描述被写在了/META-INF/MANIFEST.MF文件下,下面让我们来看看这个文件内容:

 Manifest-Version: 1.0
 Archiver-Version: Plexus Archiver
 Built-By: MrXu
 Start-Class: com.vivo.internet.nex.repeater.console.RepeaterConsoleApplication
 Spring-Boot-Classes: BOOT-INF/classes/
 Spring-Boot-Lib: BOOT-INF/lib/
 Spring-Boot-Version: 1.5.19.RELEASE
 Created-By: Apache Maven 3.8.1
 Build-Jdk: 1.8.0_281
 Main-Class: org.springframework.boot.loader.JarLauncher

文件入口的描述为Main-Class对应的value,即org.springframework.boot.loader.JarLauncher。那么,接下来我们需要看下这个类究竟做了什么?

 // JarLauncher.java
 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() {
     }
     // ...省略无关代码
     public static void main(String[] args) throws Exception {
         (new JarLauncher()).launch(args);
     }
 }

明显的main函数吸引了我们的注意,没错了,这就是入口,看看JarLauncher的空构造并没有任何代码,我们先往它的父类找找:

 // ExecutableArchiveLauncher.java
 public abstract class ExecutableArchiveLauncher extends Launcher {
     public ExecutableArchiveLauncher() {
         try {
             this.archive = this.createArchive();
         } catch (Exception var2) {
             throw new IllegalStateException(var2);
         }
     }
     // ...省略
 }
 ​
 // Launcher.java
 public abstract class Launcher {
     public Launcher() {}
     // ...省略无关代码
 }

从代码中可以看出,真正干了事情的父类是ExecutableArchiveLauncher,它在初始化时构造了archive实例,该实例封装了/META-INF/MANIFEST.MF文件的信息。后面我们也会用到它。

随后便是launch方法,我们只关系核心执行流程:

 // Launcher.java
 protected void launch(String[] args) throws Exception {
     JarFile.registerUrlProtocolHandler();
     ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives());
     this.launch(args, this.getMainClass(), classLoader);
 }
 // ExecutableArchiveLauncher.java
 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;
     }
 }

这里首先调用子类ExecutableArchiveLauncher的getMainClass方法,主要逻辑就是从/META-INF/MANIFEST.MF文件中获取Start-Class信息,对应上文就是com.vivo.internet.nex.repeater.console.RepeaterConsoleApplication字符串,这样就和我们写的启动类关联上了。

然后是launch方法的具体执行,launch()首先创建一个MainMethodRunner,将上文获取的Start-Class和透传的参数传递进去,然后调用MainMethodRunner的run方法。run方法的执行也非常简单,就是加载Start-Class对应的启动类,然后反射调用启动类的main方法。之后就是容器的初始化过程了。

 // Launcher.java
 protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
     Thread.currentThread().setContextClassLoader(classLoader);
     // 这里首先调用createMainMethodRunner创建一个MainMethodRunner实例,将mainClass和args参数传入。随后调用
     this.createMainMethodRunner(mainClass, args, classLoader).run();
 }
 protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
     return new MainMethodRunner(mainClass, args);
 }
 ​
 // MainMethodRunner.java
 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);
 }

2、总结

综上所述,对于Springboot工程,启动类并不是真正的工程入口,他会被真正的入口反射调用其main方法实现Spring容器的启动。工程入口也是Spring的开发人员为我们“营造的一种假象”,抽象出来的逻辑入口。

到此这篇关于快速掌握SpringBoot应用的启动入口的文章就介绍到这了,更多相关SpringBoot启动入口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Eclipse配置python开发环境过程图解

    Eclipse配置python开发环境过程图解

    这篇文章主要介绍了Eclipse配置python开发环境过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • JUnit单元测试入门必看篇

    JUnit单元测试入门必看篇

    下面小编就为大家分享一篇JUnit单元测试入门必看篇,对新手而言有很好的参考价值,希望对大家有所帮助
    2017-11-11
  • Spring Cloud Zuul集成Swagger实现过程解析

    Spring Cloud Zuul集成Swagger实现过程解析

    这篇文章主要介绍了Spring Cloud Zuul集成Swagger实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例

    SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例

    本文主要介绍了SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Java实现自定义Excel数据排序的方法详解

    Java实现自定义Excel数据排序的方法详解

    通常,我们可以在Excel中对指定列数据执行升序或者降序排序,在需要自定义排序情况下,我们也可以自行根据排序需要编辑数据排列顺序。本文将通过Java应用程序来实现如何自定义排序,需要的可以参考一下
    2022-09-09
  • Java使用Thread创建多线程并启动操作示例

    Java使用Thread创建多线程并启动操作示例

    这篇文章主要介绍了Java使用Thread创建多线程并启动操作,结合实例形式分析了Java基于Thread类的多线程定义与启动简单操作技巧,需要的朋友可以参考下
    2018-06-06
  • Spring Boot整合mybatis并自动生成mapper和实体实例解析

    Spring Boot整合mybatis并自动生成mapper和实体实例解析

    本文是小编给大家总结的关于Spring Boot整合mybatis并自动生成mapper和实体的内容,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-05-05
  • Java将集合List转换成String字符串(或String转换成List)详解

    Java将集合List转换成String字符串(或String转换成List)详解

    今天在写项目的时候遇到一个问题,就是要把得到的一个集合转换成字符串,下面这篇文章主要给大家介绍了关于Java将集合List转换成String字符串(或String转换成List)的相关资料,需要的朋友可以参考下
    2023-06-06
  • gradle使用maven-publish发布jar包上传到私有maven配置

    gradle使用maven-publish发布jar包上传到私有maven配置

    这篇文章主要介绍了gradle使用maven-publish发布jar包上传到私有maven的配置示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • Jackson的用法实例分析

    Jackson的用法实例分析

    这篇文章主要介绍了Jackson的用法实例分析,用于处理Java的json格式数据非常实用,需要的朋友可以参考下
    2014-08-08

最新评论