[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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • UltraEdit-32 温馨提示:右协会,取消 bak文件[通俗易懂]

    UltraEdit-32 温馨提示:右协会,取消 bak文件

    2022年1月17日
    56
  • npm使用国内淘宝镜像

    npm使用国内淘宝镜像原文地址:https://blog.csdn.net/quuqu/article/details/64121812介绍安装NodeJS之后使用npm来安装包使用的是国外的地址,经常会出现超时错误,可以通过修改为国内的淘宝源来加速安装。配置临时使用npm–registryhttps://registry.npm.taobao.orginstallexpress永久使用直…

    2022年10月23日
    0
  • hexdump -C_linux生成dump

    hexdump -C_linux生成dump描述:hexdump命令一般用来查看”二进制”文件的十六进制编码,从手册上查看,其查看的内容还要很多,诸如:ascii,decimal,hexadecimal,octal参数:hexdump[-bcCdovx][-eformat_string][-fformat_file][-nlength][-sskip]file示例:新增一个文本文件,在test文本中添加如下内容:…

    2022年9月21日
    0
  • leetcode-189. 旋转数组

    leetcode-189. 旋转数组原题链接给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。进阶:尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?示例 1:输入: nums = [1,2,3,4,5,6,7], k = 3输出: [5,6,7,1,2,3,4]解释:向右旋转 1 步: [7,1,2,3,4,5,6]向右旋转 2 步: [6,7,1,2,3,4,5]向右旋转 3 步: [5,6,7,1,2,3,4]题解

    2022年8月8日
    3
  • Linux&Docker&Mysql&GitWin常用命令

    Linux&Docker&Mysql&GitWin常用命令苦于有时候某个命令真的想不起,又得百度,干脆以后操作linux时,打开博文直接查询多爽。基于安装rabbitmq时,某个命令不会,写下的,后面再继续补充1.基本命令:删除文件:rm -rf + 名称查看当前路径: pwd复制文件:cp [ -r ] 源文件或目录 目标文件或目录复制多个文件:cp /home/usr/dir/{file1,file2,file3,file4} /home/usr/destination/显示解压文件的过程 tar -zxvf 文件名称 不显

    2022年6月13日
    26
  • hadoop工作平台梳理

    hadoop工作平台梳理

    2022年1月9日
    32

发表回复

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

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