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

目录
  • 1、一切的开始
  • 2、总结

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启动入口内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBootTest测试时不启动程序的问题

    目录 SpringBootTest测试时不启动程序 在不启动容器下,如何启动spring的机制 SpringBootTest测试时不启动程序 开发spring boot 程序过程,如果要针对某个方法做单元测试. 一般使用开发工具新建项目都会自动生成单元测试单元. 但是默认情况下的配置在测试中会启动程序,如果不想要启动可以修改如下代码 @RunWith(SpringRunner.class) @SpringBootTest public class ests { } 上面代码意思是针对所有clas

  • springboot启动后卡住无日志的几种情况小结

    目录 1.场景复现 1.1 说一下比较通用常见的场景 1.2 不太常见的场景 2.解决思路 总结一下,出现的问题场景 今天分享一下springboot启动后无日志的问题. 1.场景复现 springboot项目启动后卡住无日志,肯定是报错了或者其他原因,并且日志没有打印出来. 1.1 说一下比较通用常见的场景 检查一下 是否 exclude了springboot自带的日志包,放开后可能就有具体的错误原因了. <dependency> <groupId>org.springframe

  • Springboot 如何设置启动内存

    目录 java -jar 运行springboot项目时内存设置 例如 springboot启动命令,限制内存大小 java -jar 运行springboot项目时内存设置 java -Xms64m #JVM启动时的初始堆大小 -Xmx128m #最大堆大小 -Xmn64m #年轻代的大小,其余的空间是老年代 -XX:MaxMetaspaceSize=128m # -XX:CompressedClassSpaceSize=64m #使用 -XX:CompressedClassSpaceSize

  • springboot的四种启动方式

    目录 环境准备 第一种:直接main方法启动TxDemo2Application 第二种:通过maven插件来启动 第三种打jar包来访问 第四种通过docker容器虚拟化运行 环境准备 创建工程 pom.xml内容 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="h

  • SpringBoot实现启动类的存放位置

    目录 启动类的存放位置 启动类为什么一直报错 想记录下微服务启动类的存放位置规则 正确的存放位置是如下 SpringBoot项目启动类位置的影响 错误的位置如下 导致的结果是 成功跳转 启动类的存放位置 今天,写了一个项目,但是 启动类为什么一直报错 我是放在这个位置的,但是就一直报放在默认包错误 想记录下微服务启动类的存放位置规则 我的bug是 : 不能直接放在main/java 文件下 正确的存放位置是如下 好了,解决了. SpringBoot项目启动类位置的影响 自己闲来无事,自己搭建一个

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

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

  • idea快速搭建springboot项目的操作方法

    Spring Boot是由Pivotal团队提供的全新框架,设计目的是用来简化新Spring应用的初始搭建以及开发过程.它主要推崇的是'消灭配置',实现零配置. 那么,如何在idea中创建一个springboot项目呢? 一.在你建立的工程下创建 Module 选择Spring initializr创建. 二.在Type处选择: Maven Project(项目的构建工具) 三.创建依赖时勾上web,mybatis,mysql(这个看你个人需要吧,可以自主选择) 建立好的项目结构如下: 相对应的

  • SpringBoot中如何启动Tomcat流程

    前面在一篇文章中介绍了 Spring 中的一些重要的 context.有一些在此文中提到的 context,可以参看上篇文章. SpringBoot 项目之所以部署简单,其很大一部分原因就是因为不用自己折腾 Tomcat 相关配置,因为其本身内置了各种 Servlet 容器.一直好奇: SpringBoot 是怎么通过简单运行一个 main 函数,就能将容器启动起来,并将自身部署到其上 .此文想梳理清楚这个问题. 我们从SpringBoot的启动入口中分析: Context 创建 // Crea

  • idea使用spring Initializr 快速搭建springboot项目遇到的坑

    前言: 以前用习惯了sts,今天尝试使用IDEA 搭建springboot项目工程,却遇到了个小坑.不知道你们是否遇到过,分享如下: 首先我的maven使用的是3.5.3 首先创建springboot: 使用的是默认spring官网的镜像.这里改动不大. 选了个web. 项目创建成功 在启动类右键,没有run方法 在pom.xml上右键,将其添加为maven项目 然后发现Test模块报错. 查看pom.xml文件,发现2.3.5Release版本变红,怀疑是版本不对应问题. 创建时发现没有2.3

  • 三分钟带你了解SpringBoot真正的启动引导类

    引言 SpringBoot项目中的启动类,一般都是XXApplication,例如**「StatsApplication」,「UnionApplication」**. 每个项目的启动类名称都不一样.但是它的启动类真的是XXApplication吗? **META-INF/**Manifest.mf文件 jar文件实际上是class文件的zip压缩存档.jar并不能表达应用程序的便签信息. 「META-INF/Manifest.mf文件提供存档的便签信息.」 Manifest.mf有 「Main-

  • SpringBoot随机端口启动的实现

    目录 一.SpringBoot随机端口 1.基础介绍 2.实现步骤 二.SpringBoot多实例运行 一.SpringBoot随机端口 1.基础介绍 随机端口可以自动找指定范围内可使用的端口,不需要在配置文件中指定固定的启动端口 例如在SpringBoot中假如需要运行多个实例,则需要单独修改配置文件比较麻烦 随机端口的原理就是与对应socket端口建立连接,能连接则已被使用,反之未被使用 随机获取的端口校验可使用之后通过System.setProperty("属性名称", port

  • idea整合docker快速部署springboot应用的详细过程

    目录 一.前言 二.环境及工具 三.安装docker以及配置远程连接 四.idea连接远程docker 一.前言 容器化一词相信大家已经不陌生了,听到它我们可能会想到docker.k8s.jenkins.rancher等等.那么今天我来说一下idea如何使用docker快速部署springboot应用. 二.环境及工具 windows10(开发) centos 7.6 (部署) idea docker xshell 三.安装docker以及配置远程连接 安装docker步骤网上有很多,在这里还是

  • springboot项目突然启动缓慢的解决

    目录 springboot项目突然启动缓慢 springboot启动太慢优化 1.组件自动扫描带来的问题(@SpringBootApplication) 2.如何避免组件自动扫描带来的问题(不使用@ SpringBootApplication) 3.引发的问题--无法扫描组件 4.千古红楼只一梦,竹篮打水一场空 5.debug debug,bug bug更健康 6.分析Positive matches和Negative matches 7.再次优化配置信息 8.小结一下 springboot项目

  • Docker快速部署SpringBoot项目介绍

    1.安装 Docker 首先打开linux环境,输入以下命令进行安装: 安装 yum install docker 检查是否安装成功 docker --version #启动 systemctl start docker 如果下载很慢,可以切换到国内的阿里云镜像,进行下载: 换镜像源 sudo vim /etc/docker/daemon.json 内容如下: { "registry-mirrors": ["https://m9r2r2uj.mirror.aliyuncs.c

  • Springboot通过run启动web应用的方法

    目录 一.SpringBootApplication背后的秘密 1.@Configuration 2.@ComponentScan 3.@EnableAutoConfiguration 自动配置幕后英雄:SpringFactoriesLoader详解 二.深入探索SpringApplication执行流程 总览: 我们开发任何一个Spring Boot项目,都会用到如下的启动类 @SpringBootApplication public class Application { public st

随机推荐