Spring Boot 项目启动分析
in with 0 comment

Spring Boot 项目启动分析

in with 0 comment

建议亲自阅读源码。

Spring Boot 项目启动分析

SpringApplication 初始化分析

SpringApplication application = new SpringApplication(XyzApplication.clsss);

关键代码如下所示:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");
  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  this.webApplicationType = WebApplicationType.deduceFromClasspath();
  this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
}

参数解释

初始化流程

  1. 设置 resourceLoader 以及暂存 primarySources
  2. 从当前 jar 包中推导出 WEB 应用类型(None:非 WEB 应用,SERVLET:基于 Servlet 的 WEB 应用,REACTIVE:响应式 WEB 应用)。后续会根据 WEB 应用类型来启动相应的 WEB 服务;
  3. 使用 ClassLoader 扫描所有 META-INF/spring.factories 文件,并用 Properties 加载,最后缓存在 org.springframework.core.io.support.SpringFactoriesLoader#cache 中;
  4. 分别加载并实例化 Bootstrapper,ApplicationContextInitializer 和 ApplicationListener;
  5. 推导出主类(包含 main 方法的类)。这里利用了 Throwable 的 stackTrace 来查找的。

自定义设置

SpringApplication 中定义了不少的 setadd 方法,可以在执行 run 方法之前进行自定义设置。

例如:

application.setBannerNode(Banner.Mode.None);
application.addBootstrapper(xyz);

SpringApplication 启动分析

public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  ConfigurableApplicationContext context = null;
  configureHeadlessProperty();
  SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.starting(bootstrapContext, this.mainApplicationClass);
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    stopWatch.stop();
    if (this.logStartupInfo) {
      new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    }
    listeners.started(context);
    callRunners(context, applicationArguments);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, listeners);
    throw new IllegalStateException(ex);
  }

  try {
    listeners.running(context);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, null);
    throw new IllegalStateException(ex);
  }
  return context;
}

运行 spring application 主要目的是创建 application context 以及刷新 application context。

  1. stop watch 主要用于统计创建和刷新 application context 所消耗的时间;

    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
  2. 创建默认的 DefaultBootstrapContext,并对初始化所收集到的所有 bootstrappers 执行 initialize 方法,将 context 作为该方法的参数;

    private DefaultBootstrapContext createBootstrapContext() {
      DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
      this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
      return bootstrapContext;
    }
    
  3. 设置 java.awt.headless,如果未进行过自定义设置(如:application.setHeadless(false)、System.setProperties("java.awt.headless", false)或者 JVM 参数:java -Djava.awt.headless=true),则默认设置为 true。如果项目为非 GUI 类型的,如 console 或者 server 类型,建议设置为 true,否则设置为 false;

    private void configureHeadlessProperty() {
      System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
          System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }
    
  4. 获取所有的 org.springframework.boot.SpringApplicationRunListener(从初始化过程中的 org.springframework.core.io.support.SpringFactoriesLoader#cache 中查找),并封装在 SpringApplicationRunListeners 中。该 Listener 中主要定义了 spring application 的生命周期:starting、environmentPrepared、contextPrepared、contextLoaded、started、running 以及 failed。该配置存在于 spring-boot-x.y.z.jar!/META-INF/spring.factories 中:

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    

    初始化 EventPublishingRunListener 过程中创建了应用初始化事件管理器,并将 SpringApplication
    初始化过程中收集到的 listeners 添加进入。

    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);
    }
    
  5. 紧接着利用上一步所找到的 run listeners,执行生命周期的第一个方法 starting;

    listeners.starting(bootstrapContext, this.mainApplicationClass);
    
  6. 解析 command line args(命令参数,如 java xyz.jar --foo=bar --debug);

    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    
  7. 各种配置的准备工作;

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
      // Create and configure the environment
      ConfigurableEnvironment environment = getOrCreateEnvironment();
      configureEnvironment(environment, applicationArguments.getSourceArgs());
      ConfigurationPropertySources.attach(environment);
      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;
    }
    
    1. 根据初始化过程中所推导的 WEB 应用类型创建不同类型的可配置 Environment(ConfigurableEnvironment);
    2. 创建默认的转换器,并设置到可配置的 Environment 中;
    3. 从自定义阶段配置的 defaultProperties 以及命令行参数中获得配置(两个 PropertySource:defaultProperties(低优先级) 和 commandLineArgs(高优先级)),并添加到可配置的 Environment 中;
    4. 将当前的配置用 ConfigurationPropertySource 包装,以便在未来使用 PropertySourcesPropertyResolver 解析配置名;
    5. 通知所有监听器,环境变量已准备就绪;
    6. 将名为 defaultProperties 的 PropertySource 移动到最后;
    7. 根据自定义阶段所配置的 additionalProfiles 追加可配置的 Environment 的 activeProfiles;
    8. 将 SpringApplication 的配置设置到 Environment 中,配置前缀为:spring.main.,如 spring.main.banner-mode=console
    9. 如果未设置自定义环境,则会将此前创建的 Environment 复制(浅拷贝)一份,类型为 StandardEnvironment;
    10. 重复第 3 步。
  8. 配置 spring.beaninfo.ignore,默认设置为 true;

    configureIgnoreBeanInfo(environment);
    
  9. 根据设置的 BannerMode 决定如何打印 banner;

    Banner printedBanner = printBanner(environment);
    
  10. 根据 WEB 应用类型创建对应的 ConfigurableApplicationContext,这里仅仅是创建该实例,还并未真正准备;

    protected ConfigurableApplicationContext createApplicationContext() {
      return this.applicationContextFactory.create(this.webApplicationType);
    }
    
    switch (webApplicationType) {
    case SERVLET:
      return new AnnotationConfigServletWebServerApplicationContext();
    case REACTIVE:
      return new AnnotationConfigReactiveWebServerApplicationContext();
    default:
      return new AnnotationConfigApplicationContext();
    }
    
  11. 将 applicationStartup 设置到 ApplicationContext 中,主要是为了统计接下来的步骤所消耗的时间;

    context.setApplicationStartup(this.applicationStartup);
    
  12. 准备 BootstrapContext 和 ApplicationContext;

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
      context.setEnvironment(environment);
      postProcessApplicationContext(context);
      applyInitializers(context);
      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]));
      listeners.contextLoaded(context);
    }
    
    1. 设置此前准备好的环境到 ApplicationContext 中;

    2. 注册自定义 BeanNameGenerator 到 ApplicationContext 的 BeanFactory 中;

    3. 设置初始化所传入的 ResourceLoader;

    4. 添加 ConversionServices 到 ApplicationContext 的 BeanFactory 中;

    5. 对初始化所收集到的 ApplicationContextInitializer 执行 initialize 方法。主要是对 context 进行初始化操作(如:设置 context id、注册后续操作所需要的 bean)。例如,spring-boot.jar!/META-INF/spring.factories 文件中包含该 Initializer 的配置(我们可以重点关注这几个 context initializer):

      org.springframework.context.ApplicationContextInitializer=\
      org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
      org.springframework.boot.context.ContextIdApplicationContextInitializer,\
      org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
      org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
      org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
      
    6. 利用事件机制通知 ApplicationContext 已准备就绪(contextPrepared);

    7. 利用事件机制通知 BootstrapContext 已关闭;

    8. 打印启动日志

    9. 注册 springApplicationArgumentsspringBootBanner 到 bean factory 中;

    10. 如果未自定义 context 中的 bean factory,则默认为 org.springframework.beans.factory.support.DefaultListableBeanFactory,这里会根据自定义配置阶段所配置的 allowBeanDefinitionOverriding 设置到该 bean factory 中;

    11. 添加懒加载处理器;

    12. 从 context 中获取 BeanDefinitionRegistry,并创建 BeanDefinitionLoader,装载初始化传入的 primaryClasses;

  13. 刷新 context 信息

    protected void refresh(ConfigurableApplicationContext applicationContext) {
      applicationContext.refresh();
    }
    
    1. 尝试注册 shutdownHook,保证在线程停止后停止掉 context;
    2. 刷新准备;
    3. 刷新 BeanFactory;
    4. 对 BeanFactory 进行初始化设置;
    5. 调用 BeanFactoryPostProcessor。该处理器可访问并修改 BeanDefinition,如修改某一个 bean 所依赖的 bean 的 name;
    6. 添加 BeanPostProcessor。该处理器可在初始化前后访问到 bean;
    7. 初始化 MessageSource,为未来的参数化和国际化做好准备;
    8. 初始化 ApplicationEventMulticaster,默认会采用 org.springframework.context.event.SimpleApplicationEventMulticaster
    9. 将主动权交给子类,如果为 WEB 应用类型为 Servlet,则会启动 WebServer;
    10. 将所有的 ApplicationListener (刷新前设置到 context 中的 listeners 以及用户自定义的 listeners)设置到 ApplicationEventMulticaster 中。并顺便将 earlyApplicationEvents 分发到刚刚设置的 listener 上;
    11. 初始化剩余的 bean(非懒加载,单例);
    12. 清理资源缓存;
    13. 初始化 LifecycleProcessor;
    14. 调用 LifecycleProcessor#onRefresh;
    15. 触发 org.springframework.context.event.ContextRefreshedEvent 事件;
    16. 清理各种缓存,如反射工具的缓存、注解工具的缓存等等。
  14. 打印启动日志。这一步主要展示启动事件等信息;

  15. 通知 org.springframework.boot.SpringApplicationRunListener 服务已经启动完成;

  16. 调用用户自定义的 ApplicationRunner 以及 CommandLineRunner;

  17. 通知 org.spring.SpringApplicationRunListener 服务正在运行。

至此,Spring 项目启动完毕。