• springboot run方法执行流程分析


    目录

    SpringBoot项⽬的main函数

    1.SpringApplication()

    推断应⽤类型deduceWebApplicationType()

     getSpringFactoriesInstances();

     2. run(args)

    2.1 获取并启动监听器 

     2.2 构造应⽤上下⽂环境

    prepareEnvironment()⽅法

    2.3 初始化应⽤上下⽂

    2.4 刷新应⽤上下⽂前的准备阶段

     2.5刷新应⽤上下⽂(IOC容器的初始化过程)


    SpringBoot项⽬的main函数

    1. @SpringBootApplication
    2. public class SpringBootMytestApplication{
    3. public static void main(String[] args) {
    4. SpringApplication.run(SpringBootMytestApplication.class, args);
    5. }
    6. }

     点进run⽅法,primarySource就是引导类SpringBootMytestApplication

    1. public static ConfigurableApplicationContext run(Class primarySource, String... args) {
    2. //调用重载方法
    3. return run(new Class[]{primarySource}, args);
    4. }

    进入重载run方法

    1. public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
    2. return (new SpringApplication(primarySources)).run(args);
    3. }
    1.         SpringApplication()构造⽅法的作用加载各种配置信息,初始化各种配置对象
    2.         run(args)初始化容器

    1.SpringApplication()

    1. public SpringApplication(Class... primarySources) {
    2. this((ResourceLoader)null, primarySources);
    3. }
    4. public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    5. this.sources = new LinkedHashSet();
    6. this.bannerMode = Mode.CONSOLE;
    7. this.logStartupInfo = true;
    8. this.addCommandLineProperties = true;
    9. this.addConversionService = true;
    10. this.headless = true;
    11. this.registerShutdownHook = true;
    12. this.additionalProfiles = Collections.emptySet();
    13. this.isCustomEnvironment = false;
    14. this.lazyInitialization = false;
    15. this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    16. this.applicationStartup = ApplicationStartup.DEFAULT;
    17. //初始化资源加载器为null
    18. this.resourceLoader = resourceLoader;
    19. //断⾔,加载资源类不能为null
    20. Assert.notNull(primarySources, "PrimarySources must not be null");
    21. //初始化配置类的类名信息(格式转换)将primarySources数组转换为List,最后放到LinkedHashSet集合中
    22. this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    23. //【1.1 推断应⽤类型,后⾯会根据类型初始化对应的环境。常⽤的⼀般都是servlet环境 】
    24. this.webApplicationType = WebApplicationType.deduceFromClasspath();
    25. //【1.2 初始化classpath下 META-INF/spring.factories中已配置的BootstrapRegistryInitializer】
    26. this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    27. //【1.2 初始化classpath下META-INF/spring.factories中已配置的ApplicationContextInitializer】
    28. this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    29. //【1.3 初始化classpath下所有已配置的ApplicationListener。ApplicationListener是spring的事件监听器,典型的观察者模式,实现ApplicationListener接⼝,通过ApplicationEvent类,可以实现对spring容器全⽣命周期的监听,当然也可以⾃定义监听事件】
    30. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    31. //【1.4 根据调⽤栈,推断出 main ⽅法的类名 】
    32. this.mainApplicationClass = this.deduceMainApplicationClass();
    33. }

    推断应⽤类型deduceWebApplicationType()

    1. /**
    2. * 判断 应⽤的类型
    3. * NONE: 应⽤程序不是web应⽤,也不应该⽤web服务器去启动
    4. * SERVLET: 应⽤程序应作为基于servlet的web应⽤程序运⾏,并应启动嵌⼊式servlet web(tomcat)服务器。
    5. * REACTIVE: 应⽤程序应作为 reactive web应⽤程序运⾏,并应启动嵌⼊式reactive web服务器。
    6. * @return
    7. */
    8. static WebApplicationType deduceFromClasspath() {
    9. //classpath下必须存在org.springframework.web.reactive.DispatcherHandler
    10. if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
    11. return REACTIVE;
    12. } else {
    13. String[] var0 = SERVLET_INDICATOR_CLASSES;
    14. int var1 = var0.length;
    15. for(int var2 = 0; var2 < var1; ++var2) {
    16. String className = var0[var2];
    17. if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
    18. return NONE;
    19. }
    20. }
    21. /**classpath环境下存在javax.servlet.Servlet或者
    22. *org.springframework.web.context.ConfigurableWebApplicationContext
    23. */
    24. return SERVLET;
    25. }
    26. }

            返回类型是WebApplicationType的枚举类型, WebApplicationType 有三个枚举,三 个枚举的解释如其中注释   

            具体的判断逻辑如下:

    • WebApplicationType.REACTIVE classpath下存在org.springframework.web.reactive.DispatcherHandler
    • WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者 org.springframework.web.context.ConfigurableWebApplicationContext
    • WebApplicationType.NONE 不满⾜以上条件。

     getSpringFactoriesInstances();

            初始化classpath下 META-INF/spring.factories中已配置的类

    1. private Collection getSpringFactoriesInstances(Class type) {
    2. return this.getSpringFactoriesInstances(type, new Class[0]);
    3. }
    4. /**
    5. * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的
    6. ⼯⼚实例
    7. * @param type
    8. * @param parameterTypes
    9. * @param args
    10. * @param
    11. * @return
    12. */
    13. private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
    14. ClassLoader classLoader = this.getClassLoader();
    15. // Use names and ensure unique to protect against duplicates
    16. //通过指定的classLoader从 META-INF/spring.factories 的资源⽂件中,
    17. //读取 key 为 type.getName() 的 value
    18. Set names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    19. //创建Spring⼯⼚实例
    20. List instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    21. //对Spring⼯⼚实例排序(org.springframework.core.annotation.Order注解指定的顺序)
    22. AnnotationAwareOrderComparator.sort(instances);
    23. return instances;
    24. }

             loadFactoryNames() 这个⽅法很重要,这个⽅法是spring-core中提供的从METAINF/spring.factories中获取指定的类(key)的同⼀⼊⼝⽅法

            在这⾥,获取的是key为 org.springframework.context.ApplicationContextInitializer 的类。

     2. run(args)

            SpringBoot 启动流程最重要的部分run⽅法,初始化容器

    1. //初始化容器,得到ApplicationContext对象
    2. public ConfigurableApplicationContext run(String... args) {
    3. //记录程序开始运⾏时间
    4. long startTime = System.nanoTime();
    5. //将系统配置引导信息再次封装为上下文对象
    6. DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    7. ConfigurableApplicationContext context = null;
    8. //模拟输入输出信号,避免出现因缺少外设导致的信号传输失败,进而引发错误(模拟显示器、鼠标、键盘....),设置了一个java.awt.headless=true的信号,只是为了设备兼容
    9. this.configureHeadlessProperty();
    10. //1、获取当前注册的可运行的监听器
    11. SpringApplicationRunListeners listeners = this.getRunListeners(args);
    12. //监听器执行starting
    13. listeners.starting(bootstrapContext, this.mainApplicationClass);
    14. try {
    15. //获取参数
    16. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    17. //2、将前期读取的数据加载成了一个环境对象,用来描述信息,构造应⽤上下⽂环境
    18. ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    19. //处理需要忽略的Bean
    20. this.configureIgnoreBeanInfo(environment);
    21. //打印banner
    22. Banner printedBanner = this.printBanner(environment);
    23. //3、初始化应⽤上下⽂,创建容器对象,根据前期配置的容器类型进行判定并创建
    24. context = this.createApplicationContext();
    25. //设置启动模式
    26. context.setApplicationStartup(this.applicationStartup);
    27. //4、刷新应⽤上下⽂(容器)前的准备阶段,对容器进行设置,参数来源于前期的设定
    28. this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    29. //5、刷新应⽤上下⽂(容器)
    30. this.refreshContext(context);
    31. //刷新应⽤上下⽂(容器)后处理
    32. this.afterRefresh(context, applicationArguments);
    33. //记录程序运行时间
    34. Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    35. //判断是否记录启动时间的日志
    36. if (this.logStartupInfo) {
    37. //创建日志对象,输出日志信息,包含启动时间
    38. (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
    39. }
    40. //监听器执行started
    41. listeners.started(context, timeTakenToStartup);
    42. this.callRunners(context, applicationArguments);
    43. } catch (Throwable var12) {
    44. this.handleRunFailure(context, var12, listeners);
    45. throw new IllegalStateException(var12);
    46. }
    47. try {
    48. Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    49. //监听器执行ready
    50. listeners.ready(context, timeTakenToReady);
    51. return context;
    52. } catch (Throwable var11) {
    53. this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
    54. throw new IllegalStateException(var11);
    55. }
    56. }

             在以上的代码中,启动过程中的重要步骤共分为六步

    1. 第⼀步:获取并启动监听器
    2. 第⼆步:构造应⽤上下⽂环境
    3. 第三步:初始化应⽤上下⽂
    4. 第四步:刷新应⽤上下⽂前的准备阶段
    5. 第五步:刷新应⽤上下⽂
    6. 第六步:刷新应⽤上下⽂后的扩展接⼝ 

    2.1 获取并启动监听器 

    获取监听器

    1. private SpringApplicationRunListeners getRunListeners(String[] args) {
    2. Class[] types = new Class[]{SpringApplication.class, String[].class};
    3. return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
    4. }

     启动监听器

    1. void starting(ConfigurableBootstrapContext bootstrapContext, Class mainApplicationClass) {
    2. this.doWithListeners("spring.boot.application.starting", (listener) -> {
    3. listener.starting(bootstrapContext);
    4. }, (step) -> {
    5. if (mainApplicationClass != null) {
    6. step.tag("mainApplicationClass", mainApplicationClass.getName());
    7. }
    8. });
    9. }

     2.2 构造应⽤上下⽂环境

            应⽤上下⽂环境包括计算机的环境,Java环境,Spring的运⾏环 境,Spring项⽬的配置(在SpringBoot中就是那个熟悉的 application.properties/yml)等等。

    prepareEnvironment()⽅法

    1. private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    2. //创建并配置相应的环境
    3. ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    4. //根据⽤户配置,配置 environment系统环境
    5. this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    6. ConfigurationPropertySources.attach((Environment)environment);
    7. // 启动相应的监听器,其中⼀个重要的监听器ConfigFileApplicationListener 就是加载项⽬配置⽂件的监听器。
    8. listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
    9. DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
    10. Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
    11. this.bindToSpringApplication((ConfigurableEnvironment)environment);
    12. if (!this.isCustomEnvironment) {
    13. EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
    14. environment = environmentConverter.convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    15. }
    16. ConfigurationPropertySources.attach((Environment)environment);
    17. return (ConfigurableEnvironment)environment;
    18. }

            ⽅法中主要完成的⼯作,⾸先是创建并按照相应的应⽤类型配 置相应的环境,然后根据⽤户的配置,配置系统环境,然后启动监听器,并加载系 统配置⽂件。

    2.3 初始化应⽤上下⽂

    2.4 刷新应⽤上下⽂前的准备阶段

    1. private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    2. //设置容器环境
    3. context.setEnvironment(environment);
    4. //执⾏容器后置处理
    5. this.postProcessApplicationContext(context);
    6. //执⾏容器中的 ApplicationContextInitializer 包括spring.factories和通过三种⽅式⾃定义的
    7. this.applyInitializers(context);
    8. //向各个监听器发送容器已经准备好的事件
    9. listeners.contextPrepared(context);
    10. bootstrapContext.close(context);
    11. if (this.logStartupInfo) {
    12. this.logStartupInfo(context.getParent() == null);
    13. this.logStartupProfileInfo(context);
    14. }
    15. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    16. // Add boot specific singleton beans
    17. //将main函数中的args参数封装成单例Bean,注册进容器
    18. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    19. //将 printedBanner 也封装成单例,注册进容器
    20. if (printedBanner != null) {
    21. beanFactory.registerSingleton("springBootBanner", printedBanner);
    22. }
    23. if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
    24. ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
    25. if (beanFactory instanceof DefaultListableBeanFactory) {
    26. ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    27. }
    28. }
    29. if (this.lazyInitialization) {
    30. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    31. }
    32. context.addBeanFactoryPostProcessor(new SpringApplication.PropertySourceOrderingBeanFactoryPostProcessor(context));
    33. Set sources = this.getAllSources();
    34. Assert.notEmpty(sources, "Sources must not be empty");
    35. //加载我们的启动类,将启动类注⼊容器
    36. this.load(context, sources.toArray(new Object[0]));
    37. //发布容器已加载事件
    38. listeners.contextLoaded(context);
    39. }
    40.  2.5刷新应⽤上下⽂(IOC容器的初始化过程)

       到IoC容器的初始化过程,主要分下⾯三步:

      1. BeanDefinition的Resource定位
      2. BeanDefinition的载⼊
      3. 向IoC容器注册BeanDefinition
      1. @Override
      2. public void refresh() throws BeansException,IllegalStateException {
      3. synchronized (this.startupShutdownMonitor) {
      4. // Prepare this context for refreshing.
      5. //刷新上下⽂环境
      6. prepareRefresh();
      7. // Tell the subclass to refresh the internal bean
      8. factory.
      9. //这⾥是在⼦类中启动 refreshBeanFactory() 的地⽅
      10. @Override
      11. public void refresh() throws BeansException,
      12. IllegalStateException {
      13. synchronized (this.startupShutdownMonitor) {
      14. // Prepare this context for refreshing.
      15. //刷新上下⽂环境
      16. prepareRefresh();
      17. // Tell the subclass to refresh the internal bean
      18. factory.
      19. //这⾥是在⼦类中启动 refreshBeanFactory() 的地⽅
      20. //发布容器事件,结束Refresh过程
      21. finishRefresh();
      22. } catch (BeansException ex) {
      23. if (logger.isWarnEnabled()) {
      24. logger.warn("Exception encountered during
      25. context initialization - " +
      26. "cancelling refresh attempt: " + ex);
      27. }
      28. // Destroy already created singletons to avoid
      29. dangling resources.
      30. destroyBeans();
      31. // Reset 'active' flag.
      32. cancelRefresh(ex);
      33. // Propagate exception to caller.
      34. throw ex;
      35. } finally {
      36. // Reset common introspection caches in Spring's
      37. core, since we
      38. // might not ever need metadata for singleton
      39. beans anymore...
      40. resetCommonCaches();
      41. }
      42. }
      43. }

    41. 相关阅读:
      芯探科技--泛自动驾驶激光雷达解决方案
      2022年最新运维常用脚本学习
      前端基础之《ECMAScript 6(9)—集合介绍与API》
      数据可视化分析工具如何在国内弯道超车,迅速崛起?
      CMMI和进化论
      项目管理知识点
      1.2 变量和数据类型(Python)
      WangEditor在Vue前端的应用
      Python 实现微信测试号情侣纪念消息推送(消息群发)
      浅谈网络损伤仪HoloWAN的使用场景
    42. 原文地址:https://blog.csdn.net/m0_62520968/article/details/127010595