从0-1了解Spring是如何运行起来的(四):BeanFactory后处理初分析,了解如何加载BeanDefinition

113 阅读8分钟

前言

​ 最深刻了解一个框架的思想的方式,莫过于看源码,本系列旨在于从Springboot底层源码(Version - 2.6.6)出发,一步步了解springboot是如何运行起来的。

从0-1了解SpringBoot如何运行(一):Environment环境装配

从0-1了解SpringBoot是如何运行起来的(二):定制你的banner

从0-1了解Spring是如何运行起来的(三):Context预处理,为加载容器做准备

在前述的文章中,我们主要了解了SpringBoot是如何实现配置文件的加载、context的创建、banner图打印的流程。这次的话我们主要针对context中的refresh处理进行学习和解析。

public ConfigurableApplicationContext run(String... args) {
	......
    refreshContext(context);
	......
}

简介

本章节我们主要针对BeanDefinition的加载以及BeanFactory后处理进行学习。在展开讲解源码之前,我们需要先来了解一下这些概念。

BeanDefinition

BeanDefinition是一个描述 Bean的类实例,其其实是一个描述bean和修改bean信息的一个接口对象。如下是BeanDefinition的一个继承类图:

BeanDefinition.png

从类图中可以看到,其继承了BeanMetaDataElementAttributeAccessor。前者是Bean的元数据的存储对象,其source属性保存的是Bean实例的最基础的定义信息。而AttributeAccessor则是提供了对BeanDefinition的一些操作接口,包括获取特定的属性、移除特定属性以及添加特定属性的方法。

BeanDefinition在Spring中其实是一个相当重要的概念。其就如同Bean对象的图纸,需要生产什么样子的Bean都由BeanDefinition来定义。 同时结合类图来看,我们不难看到,BeanDefinition还有以下的一些能力:

  • 标记当前的Bean的作用范围;
  • 定义当前Bean的工厂Bean的名称;
  • 当前Bean的初始化方法名称;
  • 当前Bean是否支持懒加载
  • ... ...

通过对BeanDefinition进行加载并存储到BeanDefinitionMap中,后续Spring才能依照这些对应的“图纸”生成出匹配的Bean实例对象。

BeanFactoryPostProcessor

image-20220628153952424.png

BeanFactoryPostProcessor实际是用来处理BeanFactory的后置处理器。其生效的主要时间段是在Bean实例化之前。另外,BeanFactoryPostProcessor其实也是Spirng提供的一个钩子,其可以让你根据自己的实际情况修改Bean的定义属性。

最常见的应用就是我们的Bean中会有一些占位符,那么在Bean实例化之前这些占位符肯定是要被实际的配置参数填充的,这个填充的过程就是通过BeanFactoryPostProcessor的后置处理完成的。

源码解析:

在了解了上述的基本内容后,我们就可以从源码角度对整个bean的加载过程进行了解和分析了。

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
      // 对刷新做了一些初始化的设置 
       prepareRefresh();
      // 更新对工厂的引用 并 获取容器工厂
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 预先对BeanFactory进行相应的处理,如设置上下文的类加载器、默认的Bean等
      prepareBeanFactory(beanFactory);
      try {
         // 抽象类预留的hook方法,允许子类在标准的BeanFactory初始化后,再进行初始化。(模版设计模式)
         postProcessBeanFactory(beanFactory);
         StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
         //关键代码:会在这里调用BeanFactory的后处理器来初始化BeanDefinition,BD会在这里被加载到BDMap中。
         invokeBeanFactoryPostProcessors(beanFactory);
		......
      }catch (BeansException ex) {
		...
      }finally {
		...
      }
   }
}

Refresh(代码的整体结构采用模版设计模式实现,具体的实现由子类实现。由于Refresh部分的整体的代码块相对较长,且包含的内容较多。因此,本期我们只针对invokeBeanFactoryPostProcessors()及其之前的代码进行查看。可以看到,在执行invokeBeanFactoryPostProcessors()之前,Spring对相应的工厂做了一些处理,主要包括

1、发送context刷新的事件

2、更新工厂的引用数据

3、预处理工厂数据,如设置累加载器,注入一些默认的BeanDefinition等。

4、如果这里我们实现了预留的postProcessBeanFactory方法,那么还会执行这个模版钩子。

5、再次发送Bean初始化的事件

6、调用BeanFactory的后处理器。

这里我们跳过前面的几个步骤,直接追入最关键的源代码进行查看:

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, 			 List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
   Set<String> processedBeans = new HashSet<>();
	// 判断当前是否为BeanDef注册器
   if (beanFactory instanceof BeanDefinitionRegistry) {
      BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
      List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
      List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
         if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
            BeanDefinitionRegistryPostProcessor registryProcessor =
                  (BeanDefinitionRegistryPostProcessor) postProcessor;
            registryProcessor.postProcessBeanDefinitionRegistry(registry);
            registryProcessors.add(registryProcessor);
         }else {
            regularPostProcessors.add(postProcessor);
         }
      }
       //加载第一批次的BeanFactoryPostProcessor,并进行调用(该批次的均实现了PriOrityOrdered.class类)
      List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
      String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      for (String ppName : postProcessorNames) {
         if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            processedBeans.add(ppName);
         }
      }
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      registryProcessors.addAll(currentRegistryProcessors);
       //此处调用第一批次的BDF注册处理器。(如configClassPostProcessor,作用是加载所有的ConfigClass的BeanDef)
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
      currentRegistryProcessors.clear();

       //调用第二批次的BDF处理器(此处代码中什么都没做。。。因为没有实现ordered的类 O。O)
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      for (String ppName : postProcessorNames) {
         if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            processedBeans.add(ppName);
         }
      }
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      registryProcessors.addAll(currentRegistryProcessors);
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
      currentRegistryProcessors.clear();
		//循环对第三批次的BDF处理器进行调用,直到无处理器,这里主要是对尚未初始化的BD进行占位符等信息的替换填充,
    	//eg. MapperScannerConfigurer会对mapper的基础包路径、作用域范围进行占位符的填充。
      boolean reiterate = true;
      while (reiterate) {
         reiterate = false;
         postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
         for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName)) {
               currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
               processedBeans.add(ppName);
               reiterate = true;
            }
         }
         sortPostProcessors(currentRegistryProcessors, beanFactory);
         registryProcessors.addAll(currentRegistryProcessors);
         invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
         currentRegistryProcessors.clear();
      }
      invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
      invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
   }else {
      invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
   }
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
		//按照实现了PriorityOrder、Ordered和其余的分类方式将处理器分开
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			if (processedBeans.contains(ppName)) {
				// 如果已经处理,则跳过
			}else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

    // 首先,再次调用实现了priorityOrdered的Processor。
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    
   // 接下来再调用一次实现了ordered的Processor。
   List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
   for (String postProcessorName : orderedPostProcessorNames) {
      orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
   }
   sortPostProcessors(orderedPostProcessors, beanFactory);
   invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
   // 最后调用所有其他的后处理器
   List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
   for (String postProcessorName : nonOrderedPostProcessorNames) {
      nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
   }
   invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
   // 清除掉所有的缓存数据信息
   beanFactory.clearMetadataCache();
}

这部分的源码其实较长,整块代码主要是针对于BeanFactoryPostProcessor的执行进行操作的。这里梳理了一下整块代码的执行逻辑:

第一阶段:加载完BD前,调用初始化前的BeanFactoryPostProcessor:

  1. 调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如ConfigurationClassPostProcessor,负责加载beanFactory内的BeanDef)
  2. 调用实现了Ordered的BeanFactoryPostProcessor类。
  3. 执行剩余的所有BeanFactoryPostProcessor类。(如MapperScannerConfigure,对mapper的基本包路径、是否懒加载等进行占位符替换。)

第二阶段: 加载完BD后,调用非提前初始化的 BeanFactoryPostProcessor:

  1. 调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如PropertySourcesPlaceholderConfigurer,对所有的BeanDef的占位符进行填充)

  2. 调用实现了Ordered的BeanFactoryPostProcessor类。(如DependsOnDatabaseInitializationPostProcessor,控制DB的依赖属性)

  3. 执行剩余的所有BeanFactoryPostProcessor类。

首先,根据整个加载流程,可以比较直观的一点是,对于BeanFactory的处理,我们在某种程度上是可以控制其PostProcessor加载调用的先后顺序,这个可以通过PriorityOrder、Ordered等注解进行相应的控制。

接着,我们主要对上述的BeanFactoryPostProcessor中比较重要的两个处理器进行展开分析和学习:

ConfigurationClassPostProcessor

org.springframework.context.annotation.ConfigurationClassPostProcessor主要实现的功能是将各处的配置类及其对应的Bean的BeanDefinition扫描到对应的Factory 中。其执行的地方在第一阶段,加载的主要源代码如下所示:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   String[] candidateNames = registry.getBeanDefinitionNames();
   //获取此时的候选BeanDef的名字
   for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
         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;
   }
   // 根据对应的@Order注解的值排序对应的配置候选类
   configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
   });
   // 通过应用上下文来检测所有的自定义bean名称的生成策略。
   SingletonBeanRegistry sbr = null;
   if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if (!this.localBeanNameGeneratorSet) {
         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
               AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
         if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
         }
      }
   }
    //如果环境配置为null,则生成一个标准的环境
   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 {
      StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
      parser.parse(candidates); // 对配置的候选类进行解析,直到所有的配置类被解析完成,退出循环
      parser.validate();
      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);
      // 创建配置类BeanDefinition阅读器
      if (this.reader == null) {
         this.reader = new ConfigurationClassBeanDefinitionReader(
               registry, this.sourceExtractor, this.resourceLoader, this.environment,
               this.importBeanNameGenerator, parser.getImportRegistry());
      }
      //加载配置类BD
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);
      processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
      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());
   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();
   }
}

上述源代码的主要工作流程如下所示:

1、因为整个类的主要功能其实是对配置的类进行加载,那么首先肯定要获取到哪些是我们当前需要加载的类,即找到configCandidates(翻译为:候选的配置)

2、因为Spring本身加载配置类,也是需要一个先后顺序的,那么此时需要先根据@Order的方式来进行排序。

3、因为beanDefinition本身的名字都是由BeanNameGenerator生成的,那么这时肯定需要找到哪个才是我们需要调用的Bean的名字的生成器,从而进行后续BeanDef的名字的生成

4、实例化一个 ConfigurationClassParser ,从名字猜测,就是专门解析我们的配置类的,然后执行 parser.parse(candidates);对候选配置类进行解析,在一开始的时候,beanDefinition里面除了那些约定的类,其实只有我们自己的启动类了,而我们的启动类也是有@Configuration的,所以一开始其实就是解析我们的启动类。在解析我们的启动类的时候,不难想象,application类底下肯定还是有许多依赖的类的(通常通过@ComponentScan扫描出来),那么自然也是需要对这些类进行加载的。这里我简单的画了一下不同配置类之间的依赖关系:

image-20220703161103090.png 其实一看就是一颗典型的多叉树,那么对于多叉树的遍历,脑海中不由自主的其实会想到栈或递归。实际上,Spring也是如此做的。这个流程的大致加载逻辑如下图所示:

image-20220703113326890.png ​ 执行完了parser.parse(candidates);,其实只是将Config类的BeanDefinition给加载了,但是其中可能还有其管理的Bean实例可能都还是没有加载进来的的,那么此时就需要执行一次this.reader.loadBeanDefinitions(configClasses);对配置类管理的相关Bean进行加载。

在解析启动类之后,将解析出来的所有类再次减去已经解析的类,就会得到下一批需要解析的启动类,依次循环往返,直到解析完所有的类,那么此时加载就算完成了。

5、在加载完了所有的BeanDef后,会将元信息读取工厂的缓存清除了。由此以来,整个配置类及其依赖的BeanDef的加载流程就宣告结束。

PropertySourcesPlaceholderConfigurer

​ 对于beanDef内占位符的替换,主要由PropertySourcesPlaceholderConfigurer实现。其主要的处理步骤如下所示:

1、依据当前工厂的不同environment创建不同的propertyResolver。

2、对不同的propertyResolver设置占位符的前缀(prefix)、后缀(suffix)、以及变量的切分符(valueSeparator)。一般默认会是"${"、"}"和":"

3、根据propertyResolver再创建出BeanDefinitionVisitor,用于对不同的bean进行遍历及变量替换。

替换变量逻辑中的关键的核心代码如下所示:

protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
      StringValueResolver valueResolver) {
   BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
    //获取到所有已经加载好的Bd
   String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
   for (String curName : beanNames) {
      // 首先判断不是处理“自己”对应的BD才会进行处理
      if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
         BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
         try {
             //关键代码
            visitor.visitBeanDefinition(bd);
         }
         catch (Exception ex) {
            throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
         }
      }
   }
    //在Spring2.5中会对Alias(同名属性)也进行更新
   beanFactoryToProcess.resolveAliases(valueResolver);
   //会将属性值的处理器保存下来
   beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}

执行前:

image-20220703123500461.png

执行后:

image-20220703123515164.png

总结

相比前几章,本章从内容来说,是Spring中相对关键关键的内容,主要描述的是关于BeanDef的加载、BeanDef内占位符的数据填充等。最后让我们回过头来,再次审视一次整个refresh()代码执行的逻辑流程:

1、发送context刷新的事件

2、更新工厂的引用数据

3、预处理工厂数据,如设置类加载器,注入一些默认的BeanDefinition等。

4、如果这里我们实现了预留的postProcessBeanFactory方法,那么还会执行这个模版钩子。

5、再次发送Bean初始化的事件

6、调用BeanFactory的后处理器。

  • 第一阶段:加载完BD前,调用初始化前的BeanFactoryPostProcessor:

    1. 调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如ConfigurationClassPostProcessor,负责加载beanFactory内的BeanDef)
    2. 调用实现了Ordered的BeanFactoryPostProcessor类。
    3. 执行剩余的所有BeanFactoryPostProcessor类。(如MapperScannerConfigure,对mapper的基本包路径、是否懒加载等进行占位符替换。)
  • 第二阶段: 加载完BD后,调用非提前初始化的 BeanFactoryPostProcessor:

    1. 调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如PropertySourcesPlaceholderConfigurer,对所有的BeanDef的占位符进行填充)

    2. 调用实现了Ordered的BeanFactoryPostProcessor类。(如DependsOnDatabaseInitializationPostProcessor,控制DB的依赖属性)

    3. 执行剩余的所有BeanFactoryPostProcessor类。

归纳成流程图,整个流程就可以描述成如下的形式:

image-20220703170839119.png

参考文章

Spring IOC 有什么好处呢

Spring(四)核心容器 - BeanDefinition 解析

Spring源码-学习随笔(三)springboot的类加载beanDefinition的过程简述