Java学习资料整理-SpringBoot启动类都做了哪些事情

183 阅读3分钟

在SpringBoot的时代,已经不用像传统的springmvc一样再去配置servlet,再去配置tomcat,仅仅凭借一个main方法就可以启动服务,那么SpringBoot的启动类都做了哪些事情呢?

image.png

首先是有一个SpringBootApplication注解

image.png

这里最主要的是启用了自动装配,使得SpringBoot更好的和第三方库整合,自动装配以及自定义starter的笔记详情见 Java学习资料整理-SpringBoot自动装配 - 掘金 (juejin.cn)

那么接下来从内嵌容器和非内嵌容器的角度来看看SpringBoot的启动类都做了什么

1. 通过内嵌Servlet容器启动(java -jar)

  1. 加载运行META/MANIFEST.MF
  2. 运行JarLaunch
  3. 加载BOOT-INFO中的classes和BOOT-INFO中的lib
  4. 找到start-class

在初始化SpringApplication的时候,SpringBoot为了初始化做了以下几件事: 首先是在SpringApplication构造器内完成的操作,可以看出在run方法内部实例化了一个SpringApplication对象

image.png

首先第一步将启动类放入primarySources

image.png

第二步,推算web应用类型

this.webApplicationType = WebApplicationType.deduceFromClasspath();

第三步,读取ApplicationContextInitializer初始化容器

去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

第四步,读取ApplicationListener监听器

去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

第五步,将main方法所在的类放入mainApplicationClass中

this.mainApplicationClass = deduceMainApplicationClass();

然后实例化完成后调用了run方法,方法如图:

image.png

这个run方法大概做了以下几件事:

1、记录启动开始时间

StopWatch stopWatch = new StopWatch();
stopWatch.start();

2、开启headless

configureHeadlessProperty();

3、读取SpringApplicationRunListeners

SpringApplicationRunListeners listeners = getRunListeners(args);

4、发送ApplicationStartEvent事件

listeners.starting();

5、 封装命令行参数ApplicationArguments

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

6、读取环境变量和配置文件信息

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  • 根据webApplicationType创建环境变量对象(读取到Java环境变量和系统环境变量)
  • 配置环境变量以及确定激活的active.profile

image.png

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
   Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
   profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
   environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

7、将现有环境信息设置为@ConfigurationProperties的数据源,并且放在第一位

ConfigurationPropertySources.attach(environment);

8、发布ApplicationEnvironmentPreparedEvent事件

ConfigFileApplicationListener会读取所有默认配置文件信息

void environmentPrepared(ConfigurableEnvironment environment) {
   for (SpringApplicationRunListener listener : this.listeners) {
      listener.environmentPrepared(environment);
   }
}

9、将spring.main的配置绑定到SpringApplication属性上

bindToSpringApplication(environment);

10、将现有环境信息设置为@ConfigurationProperties的数据源,并且放在第一位

ConfigurationPropertySources.attach(environment);

11、设置忽略Bean

configureIgnoreBeanInfo(environment);

12、打印banner

Banner printedBanner = printBanner(environment);

13、实例化Spring上下文

context = createApplicationContext();

14、 初始化异常分析器

exceptionReporters = getSpringFactoriesInstances(
      SpringBootExceptionReporter.class,
      new Class[] { ConfigurableApplicationContext.class }, context);

15、准备上下文

将当前环境信息设置到Context

context.setEnvironment(environment);

调用ApplicationContextInitializer

applyInitializers(context);

发布了ApplicationContextInitializedEvent事件

listeners.contextPrepared(context);

打印启动信息和profile.active信息

if (this.logStartupInfo) {
   logStartupInfo(context.getParent() == null);
   logStartupProfileInfo(context);
}

将ApplicationArguments和printerBanner注册为Bean

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
   beanFactory.registerSingleton("springBootBanner", printedBanner);
}

设置不允许同名的Bean

if (beanFactory instanceof DefaultListableBeanFactory) {
   ((DefaultListableBeanFactory) beanFactory)
         .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}

设置是否懒加载Bean

if (this.lazyInitialization) {
   context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}

读取启动类为BeanDefinition

这里的启动类类似与XML配置文件,理解下来可以看看Spring源码中Bean的生命周期

/**
 * Load beans into the application context.
 * @param context the context to load beans into
 * @param sources the sources to load
 */
protected void load(ApplicationContext context, Object[] sources) {
   if (logger.isDebugEnabled()) {
      logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
   }
   BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
   if (this.beanNameGenerator != null) {
      loader.setBeanNameGenerator(this.beanNameGenerator);
   }
   if (this.resourceLoader != null) {
      loader.setResourceLoader(this.resourceLoader);
   }
   if (this.environment != null) {
      loader.setEnvironment(this.environment);
   }
   loader.load();
}

将SpringBoot的监听器添加到Context,并且发布ApplicationPreparedEvent事件

public void contextLoaded(ConfigurableApplicationContext context) {
   for (ApplicationListener<?> listener : this.application.getListeners()) {
      if (listener instanceof ApplicationContextAware) {
         ((ApplicationContextAware) listener).setApplicationContext(context);
      }
      context.addApplicationListener(listener);
   }
   this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

16、刷新上下文(加载IOC容器)

refreshContext(context);

到这里就调用到了Spring源码中的refresh方法,其调用方法如下

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

17、afterRefresh(待扩展)

afterRefresh(context, applicationArguments);

18、记录启动结束的时间

stopWatch.stop();
if (this.logStartupInfo) {
   new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}

19、发布ApplicationStartedEvent事件

listeners.started(context);

20、 调用ApplicationRunner和CommandLineRunner

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

21、如果启动发生异常,则发送ApplicationFailedEvent事件

handleRunFailure(context, ex, listeners);

至此,这就是SpringBoot的启动流程,其中有些代码会因版本而有一些差异,但是大致流程不变,记录下来以便自己学习。