benjming

V1

2022/06/04阅读:16主题:默认主题

SpringBoot 是如何启动的

Spring源码中我已经知道的两种上下文: ClassPathXmlApplicationContext、AnnotationConfigApplicationContext

我们知道 Spring 启动的核心过程是通过 ApplicationContext#refresh 方法,加载 BeanDefinition 在 obtainFreshBeanFactory 方法中实现。

那么 SpringBoot 是在什么时候加载 BeanDefinition?

其实SpringBoot中,是通过@Configuration注解来作为所有配置的入口标记的,例如上文中的例子,Application类被添加了@SpringBootApplication注解,而@SpringBootApplication有被@SpringBootConfiguration注解标记,而@SpringBootConfiguration正是@Configuration的子类。

SpringBoot 是如何启动的

@SpringBootApplication
public class Application {

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

}

main 方法中调用了一个方法,即 SpringApplication#run

Springboot Bean 创建过程

springboot 是如何加载 通过启动类上 @ComponentScan 注解指定的包路径来进行扫描,如果没有使用这个注解,会从启动类所在的包路径开始扫描。

springboot bean 创建过程包含的 SpringApplication run 方法中的 refresh方法。

整个加载顺序如下:

-SpringApplication#run()
--refreshContext(context)
---invokeBeanFactoryPostProcessors(beanFactory)
----ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
-----ConfigurationClassParser#parase

具体实现:

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;
}

关键看第 4 行 ConfigurableApplicationContext context = null类型的声明。 然后通过 16 行 context = createApplicationContext(); 创建了上下文,可以看下具体实现

protected ConfigurableApplicationContext createApplicationContext() {
   Class<?> contextClass = this.applicationContextClass;
   if (contextClass == null) {
      try {
         // 根据当前的应用类型,获取正确的类名
         switch (this.webApplicationType) {
         case SERVLET:
            contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
            break;
         case REACTIVE:
            contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
            break;
         default:
            contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
         }
      }
      catch (ClassNotFoundException ex) {
         throw new IllegalStateException(
               "Unable create a default ApplicationContext, "
                     + "please specify an ApplicationContextClass",
               ex);
      }
   }
   // 实例化对象
   return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

在 createApplicationContext 方法中判断了应用环境选择对应的上下文类型,并且将其实例化。 选择了上下文,接下来就是刷新上下午 refreshContext.

private void refreshContext(ConfigurableApplicationContext context) {
    this.refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }

}
image.png
image.png

我们找到了 ApplicationContext 的实现类,

Refresh 中的实现

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext)applicationContext).refresh();
}


public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            this.onRefresh();
            this.registerListeners();
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }

            this.destroyBeans();
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}

看 refesh 中 invokeBeanFactoryPostProcessors


protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

}

BeanDefinition 的实现

接着会发现 BeanDefinition 处理如下:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   // 为当前的BeanFactory生成一个id,防止同一个BeanFactory被重复处理
   int registryId = System.identityHashCode(registry);
   if (this.registriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
   }
   if (this.factoriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
   }
   this.registriesPostProcessed.add(registryId);
   // 处理配置的BeanDefinitions
   processConfigBeanDefinitions(registry);
}

继续看 processConfigBeanDefinitions(registry); 是如何处理 beanDefinition 的。

主要如下几个过程:

  1. 从当前已注册的BeanDefinition中找出有@Configuration的类作为候选集

  2. 排序

  3. 遍历候选集

    • 解析
    • 加载解析完的BeanDefinition
    • 如果有新的BeanDefinition被加载,需要判断其是否被@Configuration标记,如果是则加入候选集

具体代码如下: ConfigurationClassPostProcessor.class

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
   String[] candidateNames = registry.getBeanDefinitionNames();
   // 遍历当前容器中所有的BeanDefinition,从中寻找有@Configuration的类
   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);
         }
      }
      // 判断是否有@Configuration注解,如果有就加入候选列表
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
         configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
   }

   // 如果候选列表为空,就直接返回了
   if (configCandidates.isEmpty()) {
      return;
   }

   // 如果使用了@Order注解,就排个序
   Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
      @Override
      public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
         int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
         int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
         return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
      }
   });

   // 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 && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
         this.componentScanBeanNameGenerator = generator;
         this.importBeanNameGenerator = generator;
      }
   }

   // 下面开始解析候选列表
   // 创建一个ConfigurationClassParser,后面的解析将委托给这个对象
   ConfigurationClassParser parser = new ConfigurationClassParser(
         this.metadataReaderFactory, this.problemReporter, this.environment,
         this.resourceLoader, this.componentScanBeanNameGenerator, registry);

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
   do {
      //开始解析,细讲
      parser.parse(candidates);
      parser.validate();

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

      // 如果BeanDefinitionReader为空,则创建一个,BeanDefinitionReader是用来加载BeanDefinition
      if (this.reader == null) {
         this.reader = new ConfigurationClassBeanDefinitionReader(
               registry, this.sourceExtractor, this.resourceLoader, this.environment,
               this.importBeanNameGenerator, parser.getImportRegistry());
      }
      // 前面ConfigurationClassParser#parse处理时遗留了三类类型,只将其解析并保存,并没有注册成BeanDefinition,在这个方法中进行处理
      // 使用BeanDefinitionReader进行加载BeanDefinition,细讲
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);

      candidates.clear();
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
         // 如果发现当前容器中的BeanDefinition数量比上一轮解析完以后的数量多,说明这一轮解析了新的BeanDefinition
         // 这种情况需要对BeanDefinition列表逐个判断,如果其类型加了@Configuration注解,那么需要放入候选列表中,进行下一轮解析
         String[] newCandidateNames = registry.getBeanDefinitionNames();
         Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
         Set<String> alreadyParsedClasses = new HashSet<String>();
         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) {
      if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
         sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
      }
   }

   if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
      ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
   }
}

在解析和加载 BeanDefinition 时,需要通过特定的规则进行扫描, 主要看 ConfigurationClassParser#parseConfigurationClassBeanDefinitionReader#loadBeanDefinitions 两个方法。 看下 ConfigurationClassParser#parse 具体实现

ConfigurationClassParser#parse 实现

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
   // 依次解析所有候选BeanDefinition
   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);
      }
   }
   // 处理前面在解析@Import注解中为DeferredImportSelector子类的对象
   processDeferredImportSelectors();
}

根据不同的 BeanDefiniton 进行解析

image.png
image.png

继续深入看 parse 方法

protected final void parse(Class<?> clazz, String beanName) throws IOException {
    this.processConfigurationClass(new ConfigurationClass(clazz, beanName));
}

ConfigurationClassParser#processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
   // 如果当前类使用了@Conditional注解,则需要根据条件判断是否要跳过该类的解析
   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()) {
            //如果是被@Import注解导入的,那么记录一下
            existingClass.mergeImportedBy(configClass);
         }
         // 直接结束了,不需要重复解析
         return;
      } else {
         // 如果当前类不是被其他类通过@Import注解导入的,说明其是被显式定义的(说明一个类被定义了两次),那么将旧的移除。(重新解析一次)
         this.configurationClasses.remove(configClass);
         for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext(); ) {
            if (configClass.equals(it.next())) {
               it.remove();
            }
         }
      }
   }

   // Recursively process the configuration class and its superclass hierarchy.
   SourceClass sourceClass = asSourceClass(configClass);
   do {
      // 进行递归解析,直到该类没有父类为止,重头戏,细讲
      sourceClass = doProcessConfigurationClass(configClass, sourceClass);
   }
   while (sourceClass != null);

   this.configurationClasses.put(configClass, configClass);
}
  • 判断当前类是否需要解析,判断委托给了ConditionEvaluator类进行处理,这个类型是根据当前类的@Conditional注解进行处理的。

  • 判断当前类是否已经被加载过,如果是被@Import依赖的,那么记录一下就直接返回不重复处理了;如果不是被@Import依赖的,那么就再解析一遍(覆盖上一次的解析结果)。

  • 具体解析的调用,回调用doProcessConfigurationClass方法进行处理,可以发现这个方法被一个循环所包围,因为方法的会返回当前类型的父类,如果其父类存在,则会循环解析,知道不存在父类时,会返回null

那么看一下doProcessConfigurationClass方法的实现, 这个具体处理 Configuration 的核心方法

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
   // 递归处理当前类的内部类
   processMemberClasses(configClass, sourceClass);

   // 处理@PropertySource注解
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         // 解析PropertySource注解中的value字段,依次加载配置文件
         processPropertySource(propertySource);
      } else {
         logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // 处理@ComponentScan注解
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      // @Conditional注解优先判断
      for (AnnotationAttributes componentScan : componentScans) {
         // 根据@ComponentScan中的参数进行扫描,实际上是委托给ClassPathBeanDefinitionScanner处理
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               // 如果新解析到的BeanDefinition使用了@Configuration,直接递归解析
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // 处理@Import注解
   processImports(configClass, sourceClass,
         //提取出@Import注解需要导入的类型
         getImports(sourceClass),
         true);

   // 处理@ImportResource注解
   if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
      AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         // 将配置的资源存起来,后面会统一处理
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }

   // 处理@Bean注解
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      //解析添加了@Bean注解的放放,并存起来,后面统一处理
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // 递归查询并处理接口中的@Bean注解
   processInterfaces(configClass, sourceClass);

   // 判断是否有父类,如果有父类,则返回,外层会递归调用;如果没有则返回null,结束解析
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }

   // No superclass -> processing is complete
   return null;
}

processMemberClasses方法用来处理当前类的内部类,获取当前类的内部类,并循环递归调用processConfigurationClass方法。

可以发现该方法的代码中,依次处理了配置类中所需要处理的五个注解。

@PropertySource

@PropertySource用来实现将指定的配置文件加载到当前Spring环境中

@ComponentScan

@ComponentScan的作用是自动扫描指定包中的所有类,并根据其是否有特定注解(例如@Service@Component等)将其转化为BeanDefinition加载当上下文中。

ComponentScan注解处理方式是将注解中配置的包路径依次委托给ClassPathBeanDefinitionScanner 中进行处理, 如果解析到了新的BeanDefinition且使用了@Configuration注解,直接调用parse方法进行递归解析。

@Import

@Import可以将其他类引入当前上下文中,在该方法中,先通过getImports方法解析需要导入的类型,再调用processImports方法处理这些类型

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)) {
               // 如果导入的是ImportSelector类型,则将其实例化,并调用其selectImports获取到真实需要导入的类名
               Class<?> candidateClass = candidate.loadClass();
               ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
               ParserStrategyUtils.invokeAwareMethods(
                     selector, this.environment, this.resourceLoader, this.registry);
               if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                  // 如果是DeferredImportSelector的子类,就将其放入deferredImportSelectors中,延迟加载,后面会处理
                  this.deferredImportSelectors.add(
                        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
               } else {
                  // 非DeferredImportSelector子类,直接调用selectImports进行处理
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                  processImports(configClass, currentSourceClass, importSourceClasses, false);
               }
            }
            else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
               // Candidate class is an ImportBeanDefinitionRegistrar ->
               // delegate to it to register additional bean definitions
               // 如果导入的类型是ImportBeanDefinitionRegistrar的子类,实例化并调用其Aware接口,将这些实例存起来,后面会统一处理
               Class<?> candidateClass = candidate.loadClass();
               ImportBeanDefinitionRegistrar registrar =
                     BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
               ParserStrategyUtils.invokeAwareMethods(
                     registrar, this.environment, this.resourceLoader, this.registry);
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
            else {
               // 即不是ImportSelector也不是ImportBeanDefinitionRegistrar的情况,当做有@Configuration注解的类处理,递归解析
               this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass));
            }
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to process import candidates for configuration class [" +
               configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
         this.importStack.pop();
      }
   }
}

processImports方法中,循环处理每个需要导入的类型,根据其类型分为三种处理方式:

如果导入的类实现了ImportSelector接口:ImportSelector是一个动态导入接口,可以实现其selectImports方法,在该方法中根据条件返回最终需要导入的类。而在当前方法的实现则是会实例化这个ImportSelector子类,调用其selectImports方法获取需要导入的类型,并递归调用processImports方法。

不过此处有个例外,如果导入类实现了DeferredImportSelector接口,则不会在此处直接调用其selectImports方法,而会延迟调用,在此处只是进行记录,具体调用时机会在后文中提到。

如果不是上述的两种接口的子类:将该类当做普通的@Configuration注解的类处理,递归解析。

@ImportResource

使用@ImportResource可以将xml类型的配置导入并解析到当前项目中,但是在此处并没有真正进行解析,也将其暂存起来,在后面会统一处理。

@Bean

@Bean是通过注解方式进行Bean定义最常用的方式,在此处扫描所有加了@Bean注解的方法并将其暂存,后面统一处理。

DeferredImportSelectors接口实现

对于通过@Import导入的DeferredImportSelector子类需要延迟处理,在该方法中进行加载,处理逻辑基本与处理ImportSelector类似。

加载BeanDefinition

读者需要把思路重新回到ConfigurationClassBeanDefinitionReader#processConfigBeanDefinitions方法中,前文我们已经把ConfigurationClassParser#parse方法分析完了,接下来就是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions方法。

这里调用的方法名称为loadBeanDefinitions,直译过来就是加载BeanDefinition,但其实根据上面的阅读可以发现,前面解析时已经加载了很多BeanDefinition了,但是对于有些情况只做了记录,没有真正进行加载,而处理这些工作,正是ConfigurationClassBeanDefinitionReader#loadBeanDefinitions做的事情。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      // 再遍历一次候选类,加载之前未完成的工作
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}
private void loadBeanDefinitionsForConfigurationClass(
      ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

   if (trackedConditionEvaluator.shouldSkip(configClass)) {
      String beanName = configClass.getBeanName();
      if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
         this.registry.removeBeanDefinition(beanName);
      }
      this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
      return;
   }

   if (configClass.isImported()) {
      // 如果当前类是被@Import进来的,那么当前类型需要注册成BeanDefinition
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
   //依次加载@Bean注解的方法
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }
   // 加载@ImportResource注解对应的资源
   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   // 处理通过@Import导入的ImportBeanDefinitionRegistrar类型
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
  1. 将被@Import引入的类自身注册成BeanDefinition
  2. 将被@Bean注解的方法解析成BeanDefinition并注册
  3. 加载被@ImportResource依赖的配置文件
  4. @Import导入的ImportBeanDefinitionRegistrar类在此处处理

其中配置文件的加载,是被委托给对应的BeanDefinitionReader加载的,例如xml文件被委托给XmlBeanDefinitionReader处理,这个过程与传统的Spring项目的启动时加载配置文件的过程是一样的。

对于ImportBeanDefinitionRegistrar子类的处理过程是依次调用了其registerBeanDefinitions方法,而其子类可以在这个方法中动态加载BeanDefinition

参考资料

  • https://blog.csdn.net/liaokailin/article/details/49107209
  • http://www.bubuko.com/infodetail-3752222.html
  • https://juejin.cn/post/6940070441822191647
  • https://www.xxelin.com/2020/05/29/SpringBoot-BeanDefinition-loading/

分类:

后端

标签:

后端

作者介绍

benjming
V1