【从入门到放弃-SpringBoot】SpringBoot源码分析-启动

前言

上一篇我们一起简单了解了【从入门到放弃-MySQL】数据库连接过程分析-客户端,写完之后通读一遍,感觉分析的不是很透彻。有很多地方都没搞通,因此决定从Springboot源码开始从头研究下。

main 入口分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.springboot.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;

@MapperScan("com.springboot.demo.repository.dao")
@SpringBootApplication
@EnableScheduling
public class DemoApplication {

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

}
  • 一个简单的springboot项目启动文件中 一行代码就能搞定。
    我们从这一行代码开始看起。

SpringApplication::SpringApplication

1
2
3
4
5
6
7
8
9
10
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();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
  • 主要是初始化成员变量,比如参数列表,应用类型、监听器等。

getSpringFactoriesInstances

1
2
3
4
5
6
7
8
9
10
11
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

这个方法主要是根据type类型获取配置中默认的类列表,并进行初始化。

  • loadFactoryNames:从META-INF/spring.factories中获取type对应的配置类名称列表。
  • createSpringFactoriesInstances:校验获取到的类是否是parameterTypes类的子类,并将获取到的类通过反射机制实例化,

SpringApplication::run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(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, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

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

StopWatch

是一个计时器工具类,这里先记录了项目的启动时间

变量初始化

各种变量初始化,用法在后面具体分析

configureHeadlessProperty

设置系统java.awt.headless属性,为true的话是告知系统不要指望显示器、鼠标、键盘等可以正常运行,这是一个服务端程序,用到这些外设的时候需要靠自己模拟

getRunListeners

获取SpringApplicationRunListeners各项监听器并实例化,这些监听器配置在spring.factories资源文件中。

prepareEnvironment

配置环境,如配置文件、系统变量等并通过监听器广播环境变量准备完毕事件。

printBanner

打印启动显示的banner。

createApplicationContext

根据webApplicationType初始化spring上下文

exceptionReporters

初始化配置的异常报告类

prepareContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(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);
}
// 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);
}
  • setEnvironment:设置上下文环境
  • postProcessApplicationContext:上下文后置处理,默认没做什么事。
  • applyInitializers:调用之前实例化的几个ApplicationContextInitializer类的initialize方法
  • contextPrepared:向listeners发送上下文已准备完毕的通知。
  • load:BeanDefinitionLoader可以加载各种bean,比如注解、XML、package等多种类型的bean,在后面利用BeanDefinitionLoader将这些beans都加载进上下文中。
  • contextLoaded:向listeners发送上下文已加载完毕的通知。

refreshContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@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();
}
}
}
  • prepareRefresh:一些准备工作,比如设置启动时间、初始化资源、必须属性校验等。
  • obtainFreshBeanFactory:通知子类刷新内置的beanFactory
  • prepareBeanFactory:对容器的beanFactory做一些准备工作,比如设置classloader、设置&取消设置一些类的bean
  • invokeBeanFactoryPostProcessors:

    • 主要看invokeBeanDefinitionRegistryPostProcessors方法,会调用ConfigurationClassPostProcessor::processConfigBeanDefinitions。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
      List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
      String[] candidateNames = registry.getBeanDefinitionNames();

      for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
      ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
      if (logger.isDebugEnabled()) {
      logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
      }
      }
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
      configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
      }

      // Return immediately if no @Configuration classes were found
      if (configCandidates.isEmpty()) {
      return;
      }

      // Sort by previously determined @Order value, if applicable
      configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
      });

      // Detect any custom bean name generation strategy supplied through the enclosing application context
      SingletonBeanRegistry sbr = null;
      if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if (!this.localBeanNameGeneratorSet) {
      BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
      if (generator != null) {
      this.componentScanBeanNameGenerator = generator;
      this.importBeanNameGenerator = generator;
      }
      }
      }

      if (this.environment == null) {
      this.environment = new StandardEnvironment();
      }

      // Parse each @Configuration class
      ConfigurationClassParser parser = new ConfigurationClassParser(
      this.metadataReaderFactory, this.problemReporter, this.environment,
      this.resourceLoader, this.componentScanBeanNameGenerator, registry);

      Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
      Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
      do {
      parser.parse(candidates);
      parser.validate();

      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);

      // Read the model and create bean definitions based on its content
      if (this.reader == null) {
      this.reader = new ConfigurationClassBeanDefinitionReader(
      registry, this.sourceExtractor, this.resourceLoader, this.environment,
      this.importBeanNameGenerator, parser.getImportRegistry());
      }
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);

      candidates.clear();
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
      String[] newCandidateNames = registry.getBeanDefinitionNames();
      Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
      Set<String> alreadyParsedClasses = new HashSet<>();
      for (ConfigurationClass configurationClass : alreadyParsed) {
      alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
      }
      for (String candidateName : newCandidateNames) {
      if (!oldCandidateNames.contains(candidateName)) {
      BeanDefinition bd = registry.getBeanDefinition(candidateName);
      if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
      !alreadyParsedClasses.contains(bd.getBeanClassName())) {
      candidates.add(new BeanDefinitionHolder(bd, candidateName));
      }
      }
      }
      candidateNames = newCandidateNames;
      }
      }
      while (!candidates.isEmpty());

      // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
      if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
      sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
      }

      if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
      // Clear cache in externally provided MetadataReaderFactory; this is a no-op
      // for a shared cache since it'll be cleared by the ApplicationContext.
      ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
      }
      }

      ConfigurationClassParser:处理@Configuration/@Component等注解,扫描、注册包下的类

      ConfigurationClassBeanDefinitionReader:处理@Import/@ImportResource/@Bean等注解。

      将所有扫描到的bean都装载至registry

    • invokeBeanDefinitionRegistryPostProcessors:执行invokeBeanDefinitionRegistryPostProcessors回调

    • invokeBeanFactoryPostProcessors:执行invokeBeanFactoryPostProcessors回调
  • registerBeanPostProcessors:注册拦截创建bean的bean处理器
  • initMessageSource:初始化消息源
  • initApplicationEventMulticaster:初始化事件广播
  • onRefresh:对一些特殊子类上下文中初始化一些特殊的bean,比如在ServletWebServerApplicationContext中就做了createWebServer的操作
  • registerListeners:注册bean监听器
  • finishBeanFactoryInitialization:实例化所有单例bean,比如我们之前分析的【从入门到放弃-MySQL】数据库连接过程分析-客户端dataSource就是在这一步实例化的。

finishRefresh

结束上下文更新,并发布事件。

callRunners

如果有ApplicationRunner或者CommandLineRunner类型的bean,则触发run函数,启动任务。

总结

至此,springboot就已经启动完毕。概述下主要的启动过程就是

  • 初始化环境
  • 初始化默认配置
  • 初始化各类监听器、事件
  • 创建上下文
  • 在上下文中添加默认的bean
  • 扫描文件、注解等各种类型的bean添加在上下文中
  • 实例化各个bean
  • 启动完毕