本文分析使用的 Spring Boot 版本为 Spring Boot 2.1.18.RELEASE
 
一个简单的 Spring Boot 启动类:  
   
1 2 3 4 5 6 @SpringBootApplication public  class  DemoApplication  {    public  static  void  main (String[] args)  {         SpringApplication.run(DemoApplication.class, args);     } } 
 
@SpringBootApplication 主要包含 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan 三个注解,能够实现自动配置的是 @EnableAutoConfiguration 注解。  
1 2 3 4 5 6 7 8 9 10 11 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public  @interface  EnableAutoConfiguration {  String  ENABLED_OVERRIDE_PROPERTY  =  "spring.boot.enableautoconfiguration" ;   Class<?>[] exclude() default  {};   String[] excludeName() default  {}; } 
 
Spring 框架提供了许多以 @Enable 开头的注解,比如 @EnableScheduling、@EnableCaching、@EnableMBeanExport 等,@EnableAutoConfiguration 的理念和做事方式其实一脉相承,简单概括一下就是,借助 @Import 的支持,收集和注册特定场景相关的 bean。@EnableAutoConfiguration 就是通过 @Import 的帮助,将所有符合自动配置条件的 bean 加载到 IoC 容器中的。
Spring 对于 @Import 注解的处理是在 ConfigurationClassPostProcessor 类的 postProcessBeanFactory 方法中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  void  postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)  {  int  factoryId  =  System.identityHashCode(beanFactory);   if  (this .factoriesPostProcessed.contains(factoryId)) {     throw  new  IllegalStateException (         "postProcessBeanFactory already called on this post-processor against "  + beanFactory);   }   this .factoriesPostProcessed.add(factoryId);   if  (!this .registriesPostProcessed.contains(factoryId)) {          processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);   }   enhanceConfigurationClasses(beanFactory);   beanFactory.addBeanPostProcessor(new  ImportAwareBeanPostProcessor (beanFactory)); } 
 
所有的 BeanDefinition 在容器初始化阶段就注册了,但是有些 BeanDefinition 可能是配置类,因此需要解析这些配置类(配置类可能包含其他的 BeanDefinition)。
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 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));     }   }      if  (configCandidates.isEmpty()) {     return ;   }      configCandidates.sort((bd1, bd2) -> {     int  i1  =  ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());     int  i2  =  ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());     return  Integer.compare(i1, i2);   });      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 ();   }      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);          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);        }   while  (!candidates.isEmpty());      if  (sbr != null  && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {     sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());   }   if  (this .metadataReaderFactory instanceof  CachingMetadataReaderFactory) {     ((CachingMetadataReaderFactory) this .metadataReaderFactory).clearCache();   } } 
 
该方法会筛选出容器中所有的配置类 BeanDefinition,然后解析它们。最终所有的 BeanDefinition 都能被注册到容器中。
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 public  void  parse (Set<BeanDefinitionHolder> configCandidates)  {  for  (BeanDefinitionHolder holder : configCandidates) {     BeanDefinition  bd  =  holder.getBeanDefinition();     try  {       if  (bd instanceof  AnnotatedBeanDefinition) {         parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());       }       else  if  (bd instanceof  AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {         parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());       }       else  {         parse(bd.getBeanClassName(), holder.getBeanName());       }     }     catch  (BeanDefinitionStoreException ex) {       throw  ex;     }     catch  (Throwable ex) {       throw  new  BeanDefinitionStoreException (           "Failed to parse configuration class ["  + bd.getBeanClassName() + "]" , ex);     }   }   this .deferredImportSelectorHandler.process(); } protected  final  void  parse (@Nullable  String className, String beanName)  throws  IOException {  Assert.notNull(className, "No bean class name for configuration class bean definition" );   MetadataReader  reader  =  this .metadataReaderFactory.getMetadataReader(className);   processConfigurationClass(new  ConfigurationClass (reader, beanName)); } protected  final  void  parse (Class<?> clazz, String beanName)  throws  IOException {  processConfigurationClass(new  ConfigurationClass (clazz, beanName)); } protected  final  void  parse (AnnotationMetadata metadata, String beanName)  throws  IOException {  processConfigurationClass(new  ConfigurationClass (metadata, beanName)); } 
 
该方法主要是根据不同类型的 BeanDefinition 使用不同的解析方式。
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 protected  void  processConfigurationClass (ConfigurationClass configClass)  throws  IOException {     if  (this .conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {     return ;   }   ConfigurationClass  existingClass  =  this .configurationClasses.get(configClass);   if  (existingClass != null ) {     if  (configClass.isImported()) {       if  (existingClass.isImported()) {         existingClass.mergeImportedBy(configClass);       }              return ;     }     else  {              this .configurationClasses.remove(configClass);       this .knownSuperclasses.values().removeIf(configClass::equals);     }   }      SourceClass  sourceClass  =  asSourceClass(configClass);   do  {     sourceClass = doProcessConfigurationClass(configClass, sourceClass);   }   while  (sourceClass != null );      this .configurationClasses.put(configClass, configClass); } 
 
1 2 3 4 5 6 7 8 protected  final  SourceClass doProcessConfigurationClass (ConfigurationClass configClass, SourceClass sourceClass)     throws  IOException {            processImports(configClass, sourceClass, getImports(sourceClass), true );    } 
 
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 private  void  processImports (ConfigurationClass configClass, SourceClass currentSourceClass,     Collection<SourceClass> importCandidates, boolean  checkForCircularImports)  {  if  (importCandidates.isEmpty()) {     return ;   }   if  (checkForCircularImports && isChainedImportOnStack(configClass)) {     this .problemReporter.error(new  CircularImportProblem (configClass, this .importStack));   }   else  {     this .importStack.push(configClass);     try  {       for  (SourceClass candidate : importCandidates) {                  if  (candidate.isAssignable(ImportSelector.class)) {           Class<?> candidateClass = candidate.loadClass();           ImportSelector  selector  =  BeanUtils.instantiateClass(candidateClass, ImportSelector.class);           ParserStrategyUtils.invokeAwareMethods(               selector, this .environment, this .resourceLoader, this .registry);                      if  (selector instanceof  DeferredImportSelector) {             this .deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);           }                      else  {             String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());             Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);             processImports(configClass, currentSourceClass, importSourceClasses, false );           }         }                }     }     catch  (BeanDefinitionStoreException ex) {       throw  ex;     }     catch  (Throwable ex) {            }     finally  {       this .importStack.pop();     }   } } 
 
在该方法中,如果导入的是一个 ImportSelector,那么会调用它的 selectImports 方法获取更多需要导入到容器的类。由于 AutoConfigurationImportSelector 是一个 DeferredImportSelector,这里会执行 handle 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private  class  DeferredImportSelectorHandler  {     private  List<DeferredImportSelectorHolder> deferredImportSelectors = new  ArrayList <>();   public  void  handle (ConfigurationClass configClass, DeferredImportSelector importSelector)  {     DeferredImportSelectorHolder  holder  =  new  DeferredImportSelectorHolder (configClass, importSelector);     if  (this .deferredImportSelectors == null ) {       DeferredImportSelectorGroupingHandler  handler  =  new  DeferredImportSelectorGroupingHandler ();       handler.register(holder);       handler.processGroupImports();     }     else  {              this .deferredImportSelectors.add(holder);     }   } } 
 
在第一次调用该方法时,deferredImportSelectors 已经初始化,因此只将 DeferredImportSelectorHolder 放入其中。在回到 parse 方法时,则会调用 deferredImportSelectorHandler 的 process 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  void  process ()  {  List<DeferredImportSelectorHolder> deferredImports = this .deferredImportSelectors;      this .deferredImportSelectors = null ;   try  {     if  (deferredImports != null ) {              DeferredImportSelectorGroupingHandler  handler  =  new  DeferredImportSelectorGroupingHandler ();       deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);              deferredImports.forEach(handler::register);              handler.processGroupImports();     }   }   finally  {     this .deferredImportSelectors = new  ArrayList <>();   } } 
 
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 public  void  register (DeferredImportSelectorHolder deferredImport)  {        Class<? extends  Group > group = deferredImport.getImportSelector().getImportGroup();      DeferredImportSelectorGrouping  grouping  =  this .groupings.computeIfAbsent(       (group != null  ? group : deferredImport),       key -> new  DeferredImportSelectorGrouping (createGroup(group)));      grouping.add(deferredImport);   this .configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),       deferredImport.getConfigurationClass()); } private  static  class  DeferredImportSelectorGrouping  {     private  final  DeferredImportSelector.Group group;      private  final  List<DeferredImportSelectorHolder> deferredImports = new  ArrayList <>();   DeferredImportSelectorGrouping(Group group) {     this .group = group;   }   public  void  add (DeferredImportSelectorHolder deferredImport)  {     this .deferredImports.add(deferredImport);   }   public  Iterable<Group.Entry> getImports() {     for  (DeferredImportSelectorHolder deferredImport : this .deferredImports) {              this .group.process(deferredImport.getConfigurationClass().getMetadata(),           deferredImport.getImportSelector());     }          return  this .group.selectImports();   } } 
 
在 Spring 5 中,DeferredImportSelector 接口新增了一个内部接口 Group,这个接口的作用是对 ImportSelector 进行分组。register 方法的作用是将每个 DeferredImportSelector 存放到它对应的分组之中。在该方法中,首先调用 getImportGroup 拿到 Group 类型的类,然后通过 createGroup 方法实例化这个类,最后通过这个 Group 实例构建一个 DeferredImportSelectorGrouping 并放入 this.groupings 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  void  processGroupImports ()  {  for  (DeferredImportSelectorGrouping grouping : this .groupings.values()) {     grouping.getImports().forEach(entry -> {       ConfigurationClass  configurationClass  =  this .configurationClasses.get(entry.getMetadata());       try  {         processImports(configurationClass, asSourceClass(configurationClass),             asSourceClasses(entry.getImportClassName()), false );       }       catch  (BeanDefinitionStoreException ex) {         throw  ex;       }       catch  (Throwable ex) {         throw  new  BeanDefinitionStoreException (             "Failed to process import candidates for configuration class ["  +                 configurationClass.getMetadata().getClassName() + "]" , ex);       }     });   } } 
 
接下来调用 processGroupImports 方法。在该方法中会遍历所有的分组,每个分组分别执行对应的 process 方法和 selectImports 方法。selectImports 方法能够获取到所有经过处理的自动配置类集合,最后遍历这个集合并通过一开始我们就提到过的 processImports 方法逐个解析配置类。
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 private  static  class  AutoConfigurationGroup     implements  DeferredImportSelector .Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {   @Override    public  void  process (AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)  {          Assert.state(deferredImportSelector instanceof  AutoConfigurationImportSelector,         () -> String.format("Only %s implementations are supported, got %s" ,             AutoConfigurationImportSelector.class.getSimpleName(),             deferredImportSelector.getClass().getName()));          AutoConfigurationEntry  autoConfigurationEntry  =  ((AutoConfigurationImportSelector) deferredImportSelector)         .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);          this .autoConfigurationEntries.add(autoConfigurationEntry);     for  (String importClassName : autoConfigurationEntry.getConfigurations()) {       this .entries.putIfAbsent(importClassName, annotationMetadata);     }   }   @Override    public  Iterable<Entry> selectImports ()  {     if  (this .autoConfigurationEntries.isEmpty()) {       return  Collections.emptyList();     }          Set<String> allExclusions = this .autoConfigurationEntries.stream()         .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());          Set<String> processedConfigurations = this .autoConfigurationEntries.stream()         .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)         .collect(Collectors.toCollection(LinkedHashSet::new ));          processedConfigurations.removeAll(allExclusions);          return  sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()         .map((importClassName) -> new  Entry (this .entries.get(importClassName), importClassName))         .collect(Collectors.toList());   } } 
 
在 process 方法中会调用 getAutoConfigurationEntry 方法获取要引入的配置实体(包含所有处理过的自动配置类)。getAutoConfigurationMetadata 方法的作用是从 META-INF\spring-autoconfigure-metadata.properties 文件中获取自动配置的元数据。
1 2 3 4 5 org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration.ConditionalOnClass =com.google.gson.Gson org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration.AutoConfigureOrder =-2147483638 org.springframework.boot.autoconfigure.freemarker.FreeMarkerServletWebConfiguration.ConditionalOnClass =javax.servlet.Servlet,org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.ConditionalOnClass =javax.servlet.Servlet,org.springframework.web.servlet.config.annotation.WebMvcConfigurer,org.springframework.web.servlet.DispatcherServlet 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 protected  AutoConfigurationEntry getAutoConfigurationEntry (         AutoConfigurationMetadata autoConfigurationMetadata,         AnnotationMetadata annotationMetadata)  {    if  (!isEnabled(annotationMetadata)) {         return  EMPTY_ENTRY;     }          AnnotationAttributes  attributes  =  getAttributes(annotationMetadata);          List<String> configurations = getCandidateConfigurations(annotationMetadata,             attributes);          configurations = removeDuplicates(configurations);          Set<String> exclusions = getExclusions(annotationMetadata, attributes);     checkExcludedClasses(configurations, exclusions);     configurations.removeAll(exclusions);          configurations = filter(configurations, autoConfigurationMetadata);          fireAutoConfigurationImportEvents(configurations, exclusions);          return  new  AutoConfigurationEntry (configurations, exclusions); } 
 
该方法总的来说是对所有已知的自动配置类进行处理和筛选。
getCandidateConfigurations 方法使用了内部工具类 SpringFactoriesLoader 查找 classpath 下所有 jar 包中的 META-INF\spring.factories 文件,读取文件中 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有配置类的全限定名,保存到集合中。下面是 spring-boot-autoconfigure.jar 包中 spring.factories 文件的内容。   
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 org.springframework.context.ApplicationContextInitializer =\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener org.springframework.context.ApplicationListener =\ org.springframework.boot.autoconfigure.BackgroundPreinitializer org.springframework.boot.autoconfigure.AutoConfigurationImportListener =\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener org.springframework.boot.autoconfigure.AutoConfigurationImportFilter =\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition org.springframework.boot.autoconfigure.EnableAutoConfiguration =\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ # 以下内容过长,省略之 
 
启动过程分析 要启动 Spring Boot,一个常见的方式就是在 main 方法中调用 SpringApplication 的 run 方法,其中 primarySource 参数可以理解为带有 BeanDefinition 信息的类。一般我们会将带有 @SpringBootApplication 注解的类作为 primarySource,同时为了方便,也直接在该类中创建 main 方法。
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 public  static  ConfigurableApplicationContext run (Class<?> primarySource, String... args)  {  return  run(new  Class <?>[] { primarySource }, args); } public  static  ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args)  {  return  new  SpringApplication (primarySources).run(args); } public  SpringApplication (Class<?>... primarySources)  {  this (null , primarySources); } 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(); } 
 
这个名为 run 的类方法实际上创建了一个 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 52 53 54 55 56 57 58 59 60 61 62 63 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; } 
 
该方法中的逻辑比较清晰,但是有些逻辑并不是我们要重点关注的,这里只捡着重点的说。在容器上下文创建之前,Spring Boot 会通过 SpringFactoriesLoader 从 META-INF/spring.factories 中加载所有的 SpringApplicationRunListener,目前该接口只有一种官方实现:EventPublishingRunListener,它的作用是在容器启动的各个阶段调用对应的方法来广播对应的容器事件。
Spring Boot 在创建容器上下文时,会根据当前的环境(比如 Servlet、Reactive)来创建对应的容器上下文,在 Servlet 环境下创建的容器上下文为:AnnotationConfigServletWebServerApplicationContext。接着需要调用 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 28 29 30 31 32 33 34 35 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);   }      ConfigurableListableBeanFactory  beanFactory  =  context.getBeanFactory();   beanFactory.registerSingleton("springApplicationArguments" , applicationArguments);   if  (printedBanner != null ) {     beanFactory.registerSingleton("springBootBanner" , printedBanner);   }   if  (beanFactory instanceof  DefaultListableBeanFactory) {     ((DefaultListableBeanFactory) beanFactory)         .setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding);   }      Set<Object> sources = getAllSources();   Assert.notEmpty(sources, "Sources must not be empty" );   load(context, sources.toArray(new  Object [0 ]));      listeners.contextLoaded(context); } 
 
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 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(); } BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {   Assert.notNull(registry, "Registry must not be null" );   Assert.notEmpty(sources, "Sources must not be empty" );   this .sources = sources;      this .annotatedReader = new  AnnotatedBeanDefinitionReader (registry);      this .xmlReader = new  XmlBeanDefinitionReader (registry);   if  (isGroovyPresent()) {     this .groovyReader = new  GroovyBeanDefinitionReader (registry);   }      this .scanner = new  ClassPathBeanDefinitionScanner (registry);   this .scanner.addExcludeFilter(new  ClassExcludeFilter (sources)); } 
 
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 public  int  load ()  {  int  count  =  0 ;   for  (Object source : this .sources) {     count += load(source);   }   return  count; } private  int  load (Object source)  {  Assert.notNull(source, "Source must not be null" );      if  (source instanceof  Class<?>) {     return  load((Class<?>) source);   }      if  (source instanceof  Resource) {     return  load((Resource) source);   }      if  (source instanceof  Package) {     return  load((Package) source);   }      if  (source instanceof  CharSequence) {     return  load((CharSequence) source);   }   throw  new  IllegalArgumentException ("Invalid source type "  + source.getClass()); } private  int  load (Class<?> source)  {  if  (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {          GroovyBeanDefinitionSource  loader  =  BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);     load(loader);   }      if  (isComponent(source)) {          this .annotatedReader.register(source);     return  1 ;   }   return  0 ; } 
 
准备工作结束以后,需要执行 refreshContext 方法。在该方法中,实际执行的是 AbstractApplicationContext 的 refresh 方法,与此同时还会注册 shutdownHook。其余的工作由于不是重点,这里不再详细说明。
main 方法不退出的秘密 也许你会好奇,在 main 方法执行完毕之后,Spring Boot 为什么没有退出呢?我们知道,使用 System.exit() 或 Runtime.exit() 可以使当前 JVM 进程退出。而另一个会导致 JVM 进程退出的原因是所有的非 daemon 线程完全终止,如果根据这个条件反推的话,只要保证 Spring Boot 进程中包含至少一个非 daemon 线程一直运行,就可以保证程序不会退出。
通过跟踪代码可以发现,在调用 refresh 方法时,会调用一个叫做 onRefresh 的方法,该方法是一个空方法,由子类实现。由于在 Servlet 环境下创建的容器上下文是:AnnotationConfigServletWebServerApplicationContext,实际实现 onRefresh 方法的是它的父类:ServletWebServerApplicationContext。在该类中,onRefresh 方法会创建一个 WebServer。
1 2 3 4 5 6 7 8 9 protected  void  onRefresh ()  {  super .onRefresh();   try  {     createWebServer();   }   catch  (Throwable ex) {     throw  new  ApplicationContextException ("Unable to start web server" , ex);   } } 
 
默认情况下,这个 WebServer 也就是 Tomcat,这里最终会调用 TomcatWebServer 的 initialize 方法。
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 private  void  initialize ()  throws  WebServerException {  logger.info("Tomcat initialized with port(s): "  + getPortsDescription(false ));   synchronized  (this .monitor) {     try  {       addInstanceIdToEngineName();       Context  context  =  findContext();       context.addLifecycleListener((event) -> {         if  (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {                                 removeServiceConnectors();         }       });              this .tomcat.start();              rethrowDeferredStartupExceptions();       try  {         ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());       }       catch  (NamingException ex) {                }                     startDaemonAwaitThread();     }     catch  (Exception ex) {       stopSilently();       destroySilently();       throw  new  WebServerException ("Unable to start embedded Tomcat" , ex);     }   } } private  void  startDaemonAwaitThread ()  {  Thread  awaitThread  =  new  Thread ("container-"  + (containerCounter.get())) {     @Override      public  void  run ()  {       TomcatWebServer.this .tomcat.getServer().await();     }   };   awaitThread.setContextClassLoader(getClass().getClassLoader());   awaitThread.setDaemon(false );   awaitThread.start(); } 
 
在 initialize 方法中,调用了一个名为 startDaemonAwaitThread 的方法,从方法的注释可以看出:与 Jetty 这些容器不同,Tomcat 的所有线程都是守护线程。所以该方法创建了一个非 daemon 的线程,这个线程会一直检测 stopAwait 变量,在这个变量值为 false 时会直接休眠(调用 Thread.sleep(10000) 方法)。只有在 Tomcat 收到终止运行的信号时,该变量的值才会改变,在该线程从休眠中苏醒时会检测变量并结束运行。由于 JVM 进程中所有的非 daemon 线程都结束了运行,此时 JVM 进程也会退出。