当前位置:

首页 > 编程开发 > SpringBoot启动原理是什么

SpringBoot启动原理是什么

入口版本:2.1.8.RELEASE启动代码:@SpringBootApplcationpublicstaticvoidmain(String[]args){SpringApplication.run(BlogAdminApplication.class,args);System.out.println("========adminstartsuccess...==========");}这里传入了两个参数,BlogAdminApplication当前类和args参数我们点击进入run方法查看public

本文目录

    入口

    版本: 2.1.8.RELEASE

    启动代码:

    @SpringBootApplcation
    public static void main(String[] args) {
        SpringApplication.run(BlogAdminApplication.class, args);
        System.out.println("======== admin start success... ==========");
    }

    这里传入了两个参数,BlogAdminApplication当前类和args参数

    我们点击进入run方法查看

    public static ConfigurableApplicationContext run(Class primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

    这里是将我们写的启动类传入到了Class[]数组中,这步就是个单纯的参数转换。

    探讨primarySource参数

    那么问题是primarySource能接受的类型是啥样的,是不是什么类都可以接受,带着这个疑问,我们做一个测试,把这个参数给换成一个别的类呢,ManagerController类是一个我写的接口类

    @SpringBootApplication
    public class BlogProjectApplication {
       public static void main(String[] args) {
           SpringApplication.run(ManagerController.class, args);
           System.out.println("======== admin start success... ==========");
       }
    }

    控制台打印

    org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

    提示不能启动服务,提示缺少了ServletWebServerFactory bean 点进这个类看下

    @FunctionalInterface
    public interface ServletWebServerFactory {
        WebServer getWebServer(ServletContextInitializer... initializers);
    }

    他被FunctionalInterface标注了,是一个函数式接口,只有一个getWebServer方法,用来获取webServer的 看下他的实现类,

    SpringBoot启动原理是什么

    这不就是提示我们缺少启动的服务容器么,说的直白点,我的理解就是他缺少可以运行的容器,我们知道,没有使用springboot项目之前,我们的项目都是跑在tomcat容器上的,当然也有使用Jetty容器的。再者,我们知道SpringBoot是对tomcat进行了内置。而SpringBoot不仅仅是只有内置了tomcat,而且还内置了好多的东西,比如我们经常使用的mq、redis等等一系列的东西,这个我们可以在spring.factories配置文件中看到,这个文件位于如下位置

    SpringBoot启动原理是什么

    大概内容有下,篇幅有限,就不一一列举了。

    省略。。。
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    省略。。。

    那么回过头来,我们再看下这个问题,这些个类是如何被加载进来的,我们知道SpingBoot有个注解是开启自动注解的@EnableAutoConfiguration,他就干这个事情的。他能够激活SpringBoot内建和自定义组件的自动装配特性。

    那么,知道了这些,我们把这个之前修改后的类给改造一下,加上注解@EnableAutoConfiguration,看下执行效果。

    @RestController
    @RequestMapping("project/manager")
    @EnableAutoConfiguration
    public class ManagerController extends AbstractController {

    运行如下

    SpringBoot启动原理是什么

    从打印信息就能知道,服务器有了,只不过下面报错,提示找不到bean,那这不就简单了么,他是不是就是没有扫描到我们的包么,这里就其实可以在配置扫描包的注解继续测试,我就懒的不测试了,直接去看@SpringBootApplication注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    //
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {

    @SpringBootApplication这个注解其实就是一个组合注解,里面包含了

    • 元注解:用来标注他是一个注解的,jdk自带的

    • @SpringBootConfiguration:集成自@Configuration,表示是一个配置类

    • @EnableAutoConfiguration:激活SpringBoot内建和自定义组件的自动装配特性

    • @ComponentScan:扫描注解,添加了排除参数,指定排除了一些类

    通过这里的实验,我们可以得出结论:

    primarySource参数能接收的类是一个配置类,同时要把符合扫描规则的类装配到spring容器中,并且对SpringBoot内置的一些类进行自动扫描到,而这里的@SpringBootApplication注解就是把这些特性都整合到了一起,作为了一个引导类而已。那么说白了,primarySource他接受的其实就是一个配置类。

    关于注解详细知识的话,这里就聊这么多了,后面再详细聊。

    args参数

    args是Java命令行参数,我们在DOS中执行Java程序的时候使用“java 文件名 args参数”。args这个数组可以接收到这些参数。这个是个基础常识了。

    以下我们将继续跟踪源码进行分析

    我们继续追run()方法

    public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

    这个方法干了两个事情:

    1、new SpringApplication()来创建对象

    2、通过创建后的对象,调用对象里面的run()方法

    以下我们将从这两个地方进行分析,本篇就先研究第一个

    创建对象

    我们先看下他是怎么创建对象的,创建了哪些对象,

    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }
    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        //资源加载器
       this.resourceLoader = resourceLoader;
       //断言
       Assert.notNull(primarySources, "PrimarySources must not be null");
       //对primarySources进行存储到LinkedHashSet
       this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
       //1、推断web应用类别
       this.webApplicationType = WebApplicationType.deduceFromClasspath();
       //2、加载Spring应用上下文初始化
       setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
       //3、加载Spring应用事件监听器
       setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
       //4、推断应用引导类
       this.mainApplicationClass = deduceMainApplicationClass();
    }

    下面研究下主要流程部分

    1、推断web应用类别

    推断web应用类型属于SpringBoot应用web类型的初始化过程。而该类型也可在SpringApplication构造后,run方法执行之前,通过setWebApplicationType(WebApplicationType webApplicationType)方法进行调整。

    在推断Web应用类型的过程中,由于当前Spring应用上下文尚未准备(可在代码执行顺序中看到),所以实现采用的是检查检查当前ClassLoader下基准Class的存在性判断。

    上源码

    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
          "org.springframework.web.context.ConfigurableWebApplicationContext" };
    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";
    private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";
    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    static WebApplicationType deduceFromClasspath() {
    //当DispatcherHandler存在,且DispatcherServlet、ServletContainer两个不存在时;换言之,SpringBoot仅依赖WebFlux存在时,此时的应用类型为REACTIVE
       if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
             && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
          return WebApplicationType.REACTIVE;
       }
       //当Servlet和ConfigurableWebApplicationContext均不存在时,当前应用为非Web应用,即WebApplicationType.NONE,因为这些API均是Spring Web MVC必须的依赖
       for (String className : SERVLET_INDICATOR_CLASSES) {
          if (!ClassUtils.isPresent(className, null)) {
             return WebApplicationType.NONE;
          }
       }
       //当WebFlux和Spring Web MVC同时存在时,Web应用类型同样是Servlet Web,即WebApplicationType.SERVLET
       return WebApplicationType.SERVLET;
    }

    2、加载Spring应用上下文初始化

    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

    此过程包含两个动作,依次为getSpringFactoriesInstances(ApplicationContextInitializer.class)和setInitializers方法。 先看第一个过程

    private  Collection getSpringFactoriesInstances(Class type) {
       return getSpringFactoriesInstances(type, new Class[] {});
    }
    private  Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
       //获取类加载器
       ClassLoader classLoader = getClassLoader();
       // Use names and ensure unique to protect against duplicates
       //加载了META-INF/spring.factories资源中配置的ApplicationContextInitializer实现类名单。
       Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
       //初始化
       List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
       AnnotationAwareOrderComparator.sort(instances);
       return instances;
    }

    此处使用了Spring工厂加载机制方法SpringFactoriesLoader.loadFactoryNames(type, classLoader)。加载了META-INF/spring.factories资源中配置的ApplicationContextInitializer实现类名单。

    SpringBoot启动原理是什么

    加载完成后使用createSpringFactoriesInstances方法对其进行初始化。

    private  List createSpringFactoriesInstances(Class type, Class[] parameterTypes,
          ClassLoader classLoader, Object[] args, Set names) {
       List instances = new ArrayList<>(names.size());
       for (String name : names) {
          try {
          //从类加载器中获取指定类
             Class instanceClass = ClassUtils.forName(name, classLoader);
             //判断instanceClass是不是type的子类
             Assert.isAssignable(type, instanceClass);
             //根据以上获取的类名创建类的实例
             Constructor constructor = instanceClass.getDeclaredConstructor(parameterTypes);
             //排序
             T instance = (T) BeanUtils.instantiateClass(constructor, args);
             instances.add(instance);
          }
          catch (Throwable ex) {
             throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
          }
       }
       return instances;
    }

    3、加载Spring应用事件监听器

    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    此过程与2、加载上下文初始化基本类似。 只不过初始化的对象类型变成了ApplicationListener.class,setListeners方法也只是赋值而已

    public void setListeners(Collection> listeners) {
       this.listeners = new ArrayList<>();
       this.listeners.addAll(listeners);
    }

    SpringBoot启动原理是什么

    4、推断应用引导类

    this.mainApplicationClass = deduceMainApplicationClass(); 
    private Class deduceMainApplicationClass() {
      try {
         StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
         for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
               return Class.forName(stackTraceElement.getClassName());
            }
         }
      }
      catch (ClassNotFoundException ex) {
         // Swallow and continue
      }
      return null;
    }

    这里使用到了new RuntimeException().getStackTrace()来获取堆栈信息,找到调用执行的main方法,从而确定他的类。
    这里有个疑问:他不是传了primarySources数组,里面包含了类名么,怎么还用堆栈的方式去获取,此外,这里的堆栈获取也只能获取一个调用的主main方法,他为啥还要传一个Class数组呢?

    具体咋获取的,可以追下源码,一直跟踪他的父类Throwable,找到如下代码

    /**
     * Fills in the execution stack trace. This method records within this
     * {@code Throwable} object information about the current state of
     * the stack frames for the current thread.
     *
     * 

    If the stack trace of this {@code Throwable} {@linkplain  * Throwable#Throwable(String, Throwable, boolean, boolean) is not  * writable}, calling this method has no effect.  *  * @return  a reference to this {@code Throwable} instance.  * @see     java.lang.Throwable#printStackTrace()  */ public synchronized Throwable fillInStackTrace() {     if (stackTrace != null ||         backtrace != null /* Out of protocol state */ ) {         fillInStackTrace(0);         stackTrace = UNASSIGNED_STACK;     }     return this; } private native Throwable fillInStackTrace(int dummy);

    这里最后调用了native本地方法,去爬取线程堆栈信息,为运行时栈做一份快照。

    SpringBoot启动原理是什么

    通过这个图片,可以看到整个方法的调用链,从下往上看哦

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    springBoot项目常用目录有哪些
    springBoot项目常用目录有哪些

    springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

    解析SpringBoot和SpringMVC之间的异同
    解析SpringBoot和SpringMVC之间的异同

    解析SpringBoot和SpringMVC之间的异同SpringBoot和SpringMVC是Java领域中非常重要的开发框架。虽然它们都属于Spring框架的一部分,但是在使用和功能上有一些明显的区别。本文将对SpringBoot和SpringMVC进行比较,解析它们之间的异同。首先,让我们来了解一下SpringBoot。SpringBo

    探索SpringCloud和SpringBoot在微服务领域的不同应用方式
    探索SpringCloud和SpringBoot在微服务领域的不同应用方式

    近年来,随着云计算和分布式架构的兴起,微服务架构的应用越来越广泛。而SpringCloud和SpringBoot作为Java开发中的两个重要框架,对于微服务的实现起到了重要的作用。然而,很多人对于它们在微服务领域的不同应用方式还存在一定的疑惑。本文将从不同的角度探索SpringCloud和SpringBoot在微服务中的应用方式。首先,我们来了解一下Spri

    SpringBoot和SpringMVC有何区别?
    SpringBoot和SpringMVC有何区别?

    SpringBoot与SpringMVC的不同之处在哪里?SpringBoot和SpringMVC是两个非常流行的Java开发框架,用于构建Web应用程序。尽管它们经常分别被使用,但它们之间的不同之处也是很明显的。首先,SpringBoot可以被看作是一个Spring框架的扩展或者增强版。它旨在简化Spring应用程序的初始化和配置过程,以帮助开发人

    比较SpringBoot和SpringMVC的异同
    比较SpringBoot和SpringMVC的异同

    SpringBoot和SpringMVC都是Java开发中常用的框架,但它们之间有一些明显的差异。本文将探究这两个框架的特点和用途,并对它们的差异进行比较。首先,我们来了解一下SpringBoot。SpringBoot是由Pivotal团队开发的,它旨在简化基于Spring框架的应用程序的创建和部署。它提供了一种快速、轻量级的方式来构建独立的、可执行

    SpringBoot与SpringMVC的不同点是什么?
    SpringBoot与SpringMVC的不同点是什么?

    SpringBoot和SpringMVC是Java开发中常用的两个框架,它们都是由Spring框架所提供的,但在功能和使用方式上有着一些区别。本文将分别介绍SpringBoot和SpringMVC的特点和区别。一、SpringBoot的特点:简化配置:SpringBoot通过约定优于配置的原则,大大简化了项目的配置过程。它可以自动配置项目所需要的参数,开发人

    比较和选择指南:SpringCloud与SpringBoot的功能比较
    比较和选择指南:SpringCloud与SpringBoot的功能比较

    SpringCloud和SpringBoot是目前Java领域中最热门的开源框架,它们分别提供了一套完善的微服务架构和快速构建应用程序的解决方案。本文将对它们的功能进行比较,并给出选择指南,以帮助读者了解它们的优势和适用场景。SpringBoot是一个用于开发Java应用程序的框架,它提供了一个简化的开发流程,集成了大量常用的功能和组件,减少了开发者的工作量

    SpringCloud与SpringBoot:分析它们之间的相似与不同
    SpringCloud与SpringBoot:分析它们之间的相似与不同

    深入剖析SpringCloud和SpringBoot的异同引言:在构建企业级应用程序时,Spring框架已经成为开发者们的首选。它提供了一种简单而强大的方式来构建独立于平台的应用程序。Spring框架由两个核心模块组成:SpringBoot和SpringCloud。虽然它们都可以帮助开发者快速搭建应用程序,但它们的目标和用途存在一些差异。本文将深入剖析Spr

    从架构角度看SpringCloud与SpringBoot的差异
    从架构角度看SpringCloud与SpringBoot的差异

    从架构角度看SpringCloud与SpringBoot的差异引言:在当今的互联网时代,构建分布式系统已经成为了一种必要的需求。而SpringBoot和SpringCloud正是为了满足这个需求而诞生的。尽管它们都是由Spring框架所提供的解决方案,但从架构角度来看,它们存在着一些重要的差异。本文将从架构的角度出发,对SpringBoot和SpringCl

    Java API 开发中使用 SpringBoot 的利弊分析
    Java API 开发中使用 SpringBoot 的利弊分析

    JavaAPI开发中使用SpringBoot的利弊分析随着互联网的快速发展和技术的不断更新迭代,Java技术在开发中的广泛应用引起了人们的重视。而SpringBoot作为一种微服务架构的解决方案,已经逐渐成为Java开发中的热门选择。那么,在JavaAPI开发中使用SpringBoot,到底有哪些利弊呢?下面来分析一下。利:简化开发

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。