springboot启动流程,手把手打断点一步步看运行步骤

目录

一、让我们从启动类打断点一点点剖析启动过程

二、创建 SpringApplication

1.new SpringApplication

2.初始化参数

(1)推测web类型

(2)(扩展点)获取bootstrappers:初始启动引导器

(3)(扩展点)获取ApplicationContextInitializer初始化器

(4)(扩展点)获取ApplicationListener 应用监听器

三、运行 SpringApplication

1.进入run方法

(1)(扩展点)创建引导上下文(Context环境)

(2)bootstrapper其实是个接口

(3)(扩展点)获取所有 RunListener(运行SpringBoot的监听器)

 (4)触发SpringApplicationRunListener的starting()

(5)准备运行时环境

(6)触发SpringApplicationRunListener的environmentPrepared()

(7)打印Banner

(8)创建IOC容器

(9)准备ApplicationContext IOC容器的基本信息

(10)IOC容器的经典初始化过程

(11)触发SpringApplicationRunListener的started()

(12)(扩展点)调用ApplicationRunner和CommandLineRunner

四、总结


一、让我们从启动类打断点一点点剖析启动过程

public static void main(String[] args) {
    SpringApplication.run(WebApplicationCxf.class, args);
}

二、创建 SpringApplication

1.new SpringApplication

// org.springframework.context.ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

2.初始化参数

// org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;// 资源加载器
   Assert.notNull(primarySources, "PrimarySources must not be null");// 断言,判断是否有主配置类
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 主配置类信息保存起来(标注@SpringBootApplication的)
   this.webApplicationType = WebApplicationType.deduceFromClasspath();// 判断web应用的类型(判断是响应式还是原生的servlet工程)

   // bootstrappers:初始启动引导器(List<Bootstrapper>):去spring.factories文件中找 org.springframework.boot.Bootstrapper
   this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));

   // 找 ApplicationContextInitializer初始化器;去spring.factories找 ApplicationContextInitializer
   // 存放在List<ApplicationContextInitializer<?>> initializers中,总共7个
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

   // 找 ApplicationListener ;应用监听器。去spring.factories找 ApplicationListener
   // 存放在List<ApplicationListener<?>> listeners中,总共9个
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   // 决定谁是主程序,有main方法的类就是主程序
   this.mainApplicationClass = deduceMainApplicationClass();
}

(1)推测web类型

  1. 如果项目依赖中存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.REACTIVE
  2. 如果项目依赖中不存在org.springframework.web.reactive.DispatcherHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那么应用类型为WebApplicationType.NONE
  3. 否则,应用类型为WebApplicationType.SERVLET

(2)(扩展点)获取bootstrappers:初始启动引导器

可以在spring.factories文件中设置BootstrapRegistryInitializer的key。

这个扩展点用的比较少。

// org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   // 去spring.factories文件中找 org.springframework.boot.Bootstrapper
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

(3)(扩展点)获取ApplicationContextInitializer初始化器

Springboot中ApplicationContextInitializer的使用及源码分析

都是从spring.factories文件中查找 ApplicationContextInitializer,可能不仅局限于一个spring.factories文件

(4)(扩展点)获取ApplicationListener 应用监听器

都是从spring.factories文件中查找ApplicationListener ,可能不仅局限于一个spring.factories文件

这意味着,Spring的监听器,在Springboot中也可以使用spring.factories文件进行配置。

Spring事件详解,Spring-Event源码详解,一文搞透Spring事件管理

三、运行 SpringApplication

1.进入run方法

// org.springframework.context.ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}
// org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
   // 应用停止监听器
   StopWatch stopWatch = new StopWatch();
   stopWatch.start(); // 记录应用的启动时间

   // 创建引导上下文(Context环境)createBootstrapContext()
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   // 设置headless属性方法(java.awt.headless),让当前应用进入headless模式(自力更生模式,详情自行百度)
   configureHeadlessProperty();
   //获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 遍历 SpringApplicationRunListener 调用 starting 方法,相当于通知所有对系统正在启动过程感兴趣的人,项目正在 starting。
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      // 保存命令行参数;ApplicationArguments
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 准备运行时环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      // 打印banner
      Banner printedBanner = printBanner(environment);
      // 创建IOC容器
      // 根据项目类型(Servlet)创建容器,当前会创建 AnnotationConfigServletWebServerApplicationContext
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      // 准备ApplicationContext IOC容器的基本信息
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      // 刷新IOC容器,调用IOC容器的经典初始化过程,创建容器中的所有组件
      refreshContext(context);
      // 容器刷新完成后工作,方法是空的
      afterRefresh(context, applicationArguments);
      // 监控花费的时间
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      // 所有监听器 调用 listeners.started(context); 通知所有的监听器 started
      listeners.started(context);
      // 调用所有runners
      callRunners(context, applicationArguments);
   }
   // 如果有异常,调用Listener 的 failed方法
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      // 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
      listeners.running(context);
   }
   // running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

(1)(扩展点)创建引导上下文(Context环境)

这里就会用到之前,在spring.factories文件中设置BootstrapRegistryInitializer,虽然用处不大,但是也算一个扩展点。

private DefaultBootstrapContext createBootstrapContext() {
   DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); // 创建默认的引导上下文
   // 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
   this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
   return bootstrapContext;
}

(2)bootstrapper其实是个接口

public interface Bootstrapper {

   /**
    * Initialize the given {@link BootstrapRegistry} with any required registrations.
    * @param registry the registry to initialize
    */
   void intitialize(BootstrapRegistry registry);
}

(3)(扩展点)获取所有 RunListener(运行SpringBoot的监听器)

还是去从spring.factories文件中查找SpringApplicationRunListener。

private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   return new SpringApplicationRunListeners(logger,
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
         this.applicationStartup);
}

找到一个listener:

Listener实际是个接口,有如下方法:

监听器可以监听这些事件:

 (4)触发SpringApplicationRunListener的starting()

默认情况下SpringBoot提供了一个EventPublishingRunListener,它实现了SpringApplicationRunListener接口,默认情况下会利用EventPublishingRunListener发布一个ApplicationContextInitializedEvent事件,程序员可以通过定义ApplicationListener来消费这个事件

(5)准备运行时环境

Environment对象表示环境变量,该对象内部主要包含了:

  1. 当前操作系统的环境变量
  2. JVM的一些配置信息
  3. -D方式所配置的JVM环境变量
// org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   // Create and configure the environment
   // 返回或者创建基础环境信息对象。StandardServletEnvironment
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 配置环境信息,通过命令行参数或者配置文件获取配置属性值
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 绑定环境信息
   ConfigurationPropertySources.attach(environment);
   // 所有监听器遍历调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
   listeners.environmentPrepared(bootstrapContext, environment);
   DefaultPropertiesPropertySource.moveToEnd(environment);
   // 激活额外的环境
   configureAdditionalProfiles(environment);
   // 绑定一些值
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

(6)触发SpringApplicationRunListener的environmentPrepared()

默认情况下会利用EventPublishingRunListener发布一个ApplicationEnvironmentPreparedEvent事件,程序员可以通过定义ApplicationListener来消费这个事件,比如默认情况下会有一个EnvironmentPostProcessorApplicationListener来消费这个事件,而这个ApplicationListener接收到这个事件之后,就会解析application.properties、application.yml文件,并添加到Environment对象中去。

(7)打印Banner

SpringBoot自定义banner,如何定制炫酷的banner提升项目B格?

(8)创建IOC容器

// org.springframework.boot.SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
   // 根据项目类型(Servlet)创建容器,当前会创建 AnnotationConfigServletWebServerApplicationContext
   return this.applicationContextFactory.create(this.webApplicationType);
}

会利用ApplicationContextFactory.DEFAULT,根据应用类型创建对应的Spring容器。

ApplicationContextFactory.DEFAULT为:

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
    try {
        switch (webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
        }
    }
    catch (Exception ex) {
        throw new IllegalStateException("Unable create a default ApplicationContext instance, "
                                        + "you may need a custom ApplicationContextFactory", ex);
    }
};

所以:

  1. 应用类型为SERVLET,则对应AnnotationConfigServletWebServerApplicationContext
  2. 应用类型为REACTIVE,则对应AnnotationConfigReactiveWebServerApplicationContext
  3. 应用类型为普通类型,则对应AnnotationConfigApplicationContext

(9)准备ApplicationContext IOC容器的基本信息

默认情况下SpringBoot提供了多个ApplicationContextInitializer,其中比较重要的有ConditionEvaluationReportLoggingListener,别看到它的名字叫XXXListener,但是它确实是实现了ApplicationContextInitializer接口的。

在它的initialize()方法中会:

  1. 将Spring容器赋值给它的applicationContext属性
  2. 并且往Spring容器中添加一个ConditionEvaluationReportListener(ConditionEvaluationReportLoggingListener的内部类),它是一个ApplicationListener
  3. 并生成一个ConditionEvaluationReport对象赋值给它的report属性

ConditionEvaluationReportListener会负责接收ContextRefreshedEvent事件,也就是Spring容器一旦启动完毕就会触发ContextRefreshedEvent,ConditionEvaluationReportListener就会打印自动配置类的条件评估报告。

// org.springframework.boot.SpringApplication#prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   // 保存基础环境信息
   context.setEnvironment(environment);
   // IOC容器的后置处理流程(注册一些组件、读取配置文件资源、注册资源加载器、准备类型转换器等等)
   postProcessApplicationContext(context);
   // 应用初始化器:applyInitializers
   // 遍历所有的 ApplicationContextInitializer 。调用 initialize方法。来对ioc容器进行初始化扩展功能
   applyInitializers(context);
   // 遍历所有的 listener 调用 contextPrepared方法。
   listeners.contextPrepared(context);
   bootstrapContext.close(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
   // 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded
   listeners.contextLoaded(context);
}

(10)IOC容器的经典初始化过程

spring容器创建流程参考博文

spring系列-注解驱动原理及源码-spring容器创建流程-CSDN博客

注意:

spring默认的onRefresh()方法是空的,springboot将onRefresh()方法重写,加入了tomcat的启动。

// org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
protected void onRefresh() {
   super.onRefresh();
   try {
      // 创建一个web应用
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

springboot嵌入式tomcat启动原理参考博文:

https://blog.csdn.net/A_art_xiang/article/details/122435665

(11)触发SpringApplicationRunListener的started()

发布ApplicationStartedEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示状态变更状态,变更后的状态为LivenessState.CORRECT

LivenessState枚举有两个值:

  1. CORRECT:表示当前应用正常运行中
  2. BROKEN:表示当前应用还在运行,但是内部出现问题,暂时还没发现哪里用到了

(12)(扩展点)调用ApplicationRunner和CommandLineRunner

  1. 获取Spring容器中的ApplicationRunner类型的Bean
  2. 获取Spring容器中的CommandLineRunner类型的Bean
  3. 执行它们的run()

// org.springframework.boot.SpringApplication#callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   // 获取容器中的 ApplicationRunner
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   // 获取容器中的 CommandLineRunner
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   // 合并所有runner并且按照@Order进行排序
   AnnotationAwareOrderComparator.sort(runners);
   // 遍历所有的runner。调用 run 方法
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

runner是一个接口:

@FunctionalInterface
public interface ApplicationRunner {

   /**
    * Callback used to run the bean.
    * @param args incoming application arguments
    * @throws Exception on error
    */
   void run(ApplicationArguments args) throws Exception;
}

四、总结

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用VC(Visual C++)可以进行图形用户界面(GUI)应用程序的开发。下面是一个简单的步骤指南来教你如何用VC进行开发。 1. 安装VC:首先,你需要下载和安装VC的开发环境。你可以从微软官方网站上下载VC的最新版本,然后按照安装向导进行安装。 2. 创建新项目:打开VC的开发环境,然后创建一个新的项目。你可以选择创建一个空项目或者使用已有的模板来快速构建应用程序。 3. 设计用户界面:使用VC的可视化设计器来设计应用程序的用户界面。你可以从工具箱中拖拽控件(如按钮、文本框、列表框等)到窗口上,然后设置它们的属性和布局。 4. 编写代码:使用VC的代码编辑器,在事件处理函数中编写代码来实现特定的功能。例如,当按钮被点击时,可以编写相应的代码来触发一些操作,比如显示消息框、打开文件等。 5. 调试和测试:在开发过程中,经常使用调试工具来检查代码的执行过程和变量的值。VC提供了强大的调试功能,可以帮助你排查和修复代码中的错误。 6. 编译和构建:当你完成了应用程序的开发,可以进行编译和构建操作。VC会将你的代码编译成可执行文件或者库文件,供用户使用。 7. 发布和部署:最后,你可以将应用程序发布和部署到目标设备或者将其打包成安装程序。这样用户就可以在他们自己的计算机上安装和运行你的应用程序了。 通过上述步骤,你可以利用VC开发出各种各样的应用程序,包括桌面软件、图形绘制工具、数据库管理工具等。当然,在实际开发中还有很多细节需要学习和掌握,因此你可以参考更多的教程和文档来进一步提升你的技能。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了也弱了。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值