前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot 是如何启动的

SpringBoot 是如何启动的

作者头像
王小明_HIT
发布2022-06-14 16:32:06
6400
发布2022-06-14 16:32:06
举报
文章被收录于专栏:程序员奇点

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

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

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

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

SpringBoot 是如何启动的

代码语言:javascript
复制
@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方法。

整个加载顺序如下:

代码语言:javascript
复制
-SpringApplication#run()
--refreshContext(context)
---invokeBeanFactoryPostProcessors(beanFactory)
----ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
-----ConfigurationClassParser#parase

具体实现:

代码语言:javascript
复制
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(); 创建了上下文,可以看下具体实现

代码语言:javascript
复制
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.

代码语言:javascript
复制
private void refreshContext(ConfigurableApplicationContext context) {
    this.refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }

}

image.png

我们找到了 ApplicationContext 的实现类,

Refresh 中的实现

代码语言:javascript
复制
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

代码语言:javascript
复制

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 处理如下:

代码语言:javascript
复制
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

代码语言:javascript
复制
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 实现

代码语言:javascript
复制
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

继续深入看 parse 方法

代码语言:javascript
复制
protected final void parse(Class<?> clazz, String beanName) throws IOException {
    this.processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
ConfigurationClassParser#processConfigurationClass
代码语言:javascript
复制
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 的核心方法

代码语言:javascript
复制
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方法处理这些类型

代码语言:javascript
复制
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做的事情。

代码语言:javascript
复制
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      // 再遍历一次候选类,加载之前未完成的工作
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}
代码语言:javascript
复制
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/
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员奇点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SpringBoot 是如何启动的
  • Springboot Bean 创建过程
  • Refresh 中的实现
  • BeanDefinition 的实现
  • ConfigurationClassParser#parse 实现
    • ConfigurationClassParser#processConfigurationClass
      • @PropertySource
        • @ComponentScan
          • @Import
            • @ImportResource
              • @Bean
                • DeferredImportSelectors接口实现
                • 加载BeanDefinition
                • 参考资料
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档