[springboot]springboot启动流程[通俗易懂]

[springboot]springboot启动流程[通俗易懂]SpringBoot程序有一个入口,就是main方法。main里面调用SpringApplication.run()启动整个SpringBoot程序,该方法所在类需要使用@SpringBootApplication复合注解。其中需要关注的是:@SpringBootApplication注解其实是包含了三个注解:@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置。简单概括一下就是,是借助@Import的帮助,将所有符合自动配

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

Spring Boot程序有一个入口,就是main方法。main里面调用SpringApplication.run()启动整个Spring Boot程序,该方法所在类需要使用@SpringBootApplication复合注解。

1、@SpringBootApplication注解的组成

@EnableAutoConfiguration

@EnableAutoConfiguration是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器

在这个注解中,最重要的是它导入了一个类EnableAutoConfigurationImportSelector,它是一个ImportSelector接口的实现类

Spring Boot在启动时,在classpath下所有的META-INF/spring.factories文件中根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration作为查找的Key,获得对应的一组@Configuration类,并将其封装到一个List中返回。(spring.factories是一个典型的java properties文件,只不过Key和Value都是Java类型的完整类名)

通过 filter 过滤掉当前环境不需要自动装配的类,比如没有集成RabbitMQ,就不需要,或者有的条件@Conditional不满足也不需要自动装配

将需要自动装配的全路径类名注册到 SpringIOC 容器,自此 SpringBoot 自动装配完成!

@SpringBootConfiguration

该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration和@ComponentScan的处理,本质上与@Configuration注解没有区别。

@ComponentScan

@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

2、SpringBoot启动流程

1、SpringBoot启动的时候,会构造一个SpringApplication的实例,然后调用这个实例的run方法,在run方法调用之前,也就是构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:

  1. 把参数sources设置到SpringApplication属性中,这个sources可以是任何类型的参数.
  2. 判断是否是web程序,并设置到webEnvironment的boolean属性中.
  3. 创建并初始化ApplicationInitializer,设置到initializers属性中 。
  4. 创建并初始化ApplicationListener,设置到listeners属性中 。
  5. 初始化主类mainApplicatioClass。

源代码:

private void initialize(Object[] sources) { 
   
      if (sources != null && sources.length > 0) { 
   
          //把sources设置到SpringApplication的sources属性中,目前只是一个MyApplication类对象
        this.sources.addAll(Arrays.asList(sources)); 
      }
          //判断是否是web程序,并设置到webEnvironment的boolean属性中
      this.webEnvironment = deduceWebEnvironment(); 
          //找出所有的初始化器,默认有5个,设置到initializers属性中 。
      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
          //找出所有的应用程序监听器,默认有9个,设置到listeners属性中 。
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
          //找出运行的主类(main class)
      this.mainApplicationClass = deduceMainApplicationClass();
    }

2、SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:

  1. 构造一个StopWatch计时器,用来记录SpringBoot的启动时间 。
  2. 获取SpringApplicationRunListeners并封装到SpringApplicationRunListeners中启动,用于监听run方法的执行。
  3. 创建并初始化ApplicationArguments,获取run方法传递的args参数。
  4. 创建并初始化ConfigurableEnvironment(环境配置)。
  5. 打印banner和版本。
  6. 构造Spring容器(ApplicationContext)上下文。
  7. SpringApplicationRunListeners发布finish事件。
  8. StopWatch计时器停止计时,日志打印总共启动的时间。
  9. 发布SpringBoot程序已启动事件(started())
  10. 调用ApplicationRunner和CommandLineRunner
  11. 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了(running())
public ConfigurableApplicationContext run(String... args) { 
   
    // 创建一个StopWatch实例,用来记录SpringBoot的启动时间
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 ConfigurableApplicationContext context = null;
 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 configureHeadlessProperty();
    // 通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
 SpringApplicationRunListeners listeners = getRunListeners(args);
    // 发布SprintBoot启动事件:ApplicationStartingEvent
 listeners.starting();
 try { 
   
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     // 创建和配置environment,发布事件:SpringApplicationRunListeners#environmentPrepared
  ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  configureIgnoreBeanInfo(environment);
     // 打印SpringBoot的banner和版本
  Banner printedBanner = printBanner(environment);
     // 创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
  context = createApplicationContext();
  exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    new Class[] { 
    ConfigurableApplicationContext.class }, context);
     // 准备ApplicationContext,Initializers设置到ApplicationContext后发布事件:ApplicationContextInitializedEvent
     // 打印启动日志,打印profile信息(如dev, test, prod)
     // 调用EventPublishingRunListener发布ApplicationContext加载完毕事件:ApplicationPreparedEvent
  prepareContext(context, environment, listeners, applicationArguments, printedBanner);
     // 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作
     // 以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
  refreshContext(context);
     // hook方法
  afterRefresh(context, applicationArguments);
     // stopWatch停止计时,日志打印总共启动的时间
  stopWatch.stop();
  if (this.logStartupInfo) { 
   
   new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  }
     // 发布SpringBoot程序已启动事件ApplicationStartedEvent
  listeners.started(context);
     // 调用ApplicationRunner和CommandLineRunner
  callRunners(context, applicationArguments);
 }
 catch (Throwable ex) { 
   
  handleRunFailure(context, ex, exceptionReporters, listeners);
  throw new IllegalStateException(ex);
 }

 try { 
   
     // 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
  listeners.running(context);
 }
 catch (Throwable ex) { 
   
  handleRunFailure(context, ex, exceptionReporters, null);
  throw new IllegalStateException(ex);
 }
 return context;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/171209.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • #转载 基于C#通过OPC UA/DA访问PLC学习网站

    #转载 基于C#通过OPC UA/DA访问PLC学习网站#转载#基于C#通过OPCUA/DA访问PLC学习网站今年刚入职的新手,第一次接触OPCUA、OPCDA、C#、PLC,全靠各种百度,经过一个多星期的摸索,基本算是刚入门吧,下面把学习过程中收藏的一些实用的文章和大家分享一下,绝对可以让你少走很多弯路。1.C#读写opcua服务器,浏览所有节点,读写节点,读历史数据,调用方法,订阅,批量订阅操作-dathlin2.C#OPCUA服务器OPCUA网关三菱西门子欧姆龙Modbus转OPCUA服务器可配置的OPCUA服务器网

    2022年10月18日
    5
  • chromedriver怎么下载_chrome国内下载地址

    chromedriver怎么下载_chrome国内下载地址1、亲测能正常下载运用,具体地址如下:http://chromedriver.storage.proxy.ustclug.org/index.html选择对应版本,一定要与本地谷歌浏览器版本一致,要不在运行webdriver时会报错window10的环境,打开后选择win32的zip包应用就行,无论你是64位还是32位都选这个就ok:…

    2022年9月19日
    4
  • 有极性电容和无极性电容的区别_非极性电容

    有极性电容和无极性电容的区别_非极性电容有极性电容与无极性电容的概述有极性电容与无极性电容的概述有极性电容的识别有极性电容于无极性电容的区别网友见解有极性电容与无极性电容的概述理想的电容,本来是没有极性的。但是在实际中,为了获得大容量,就使用了某些特殊的材料和结构,这就导致了实际的电容有些是有极性的。常见的有极性电容有铝电解电容,钽电解电容等。电解电容一般是容量相对比较大的。如果要做一个大容量的无极性电容,就没那…

    2022年8月22日
    9
  • ThinkPHP 模版中的内置标签

    ThinkPHP 模版中的内置标签

    2021年10月21日
    197
  • Ubuntu20.04安装中文输入法

    Ubuntu20.04安装中文输入法新装了ubuntu20.04的系统,由于装系统时,选择安装了英文的版本,因此系统没有中文输入法,此时我想加入中文输入法。在设置-语言中怎么改都没有用。最终用下面方法解决了安装中文语言包**由于我装系统时,选择了安装English版本,所以先安装了一下中文语言包。sudoapt-getupdatesudoapt-getinstalllanguage-pack-zh-hans安装输入法sudoaptinstallibus-libpinyinsudoapti

    2022年9月25日
    4
  • Java中如何将int 类型转换为 Long类型

    Java中如何将int 类型转换为 Long类型版权声明:本文由施勇原创,转载请注明作者和出处!   https://blog.csdn.net/shiyong1949/article/details/52687955Longl=(Long)3;1很多同学可能会用上面的方法将int类型转换为Long类型,但事实上这样是不可行的。因为Long是包装类,而int是值类型数据,两者是不能这样强转的。longl=(long)3;…

    2022年5月3日
    2.0K

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号