Spring 源码阅读 26:Bean 早期实例处理及属性注入

294 阅读11分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第35天,点击查看活动详情

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 25:单例 Bean 实例的创建过程(2)

前情提要

前两篇,从doCreateBean方法开始,一直分析到 Spring 通过反射机制创建 Bean 实例对象的步骤,至此,已经得到了 Bean 的实例对象。不过对于doCreateBean方法来说,这只完成了第一个步骤。对于创建好的 Bean 对象,还有很多后续的处理工作。

本文将接着上一篇,继续分析doCreateBean方法后续的代码。

MergedBeanDefinitionPostProcessor

得到 Spring 创建的原始 Bean 对象后,它会被包装到一个 BeanWrapper 中并返回到doCreateBean,接着看之后的逻辑:

// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
   if (!mbd.postProcessed) {
      try {
         applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      }
      catch (Throwable ex) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Post-processing of merged bean definition failed", ex);
      }
      mbd.postProcessed = true;
   }
}

这里,通过applyMergedBeanDefinitionPostProcessors方法执行了一些后处理操作,我们具体看一下:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof MergedBeanDefinitionPostProcessor) {
         MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
         bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
      }
   }
}

这里又看到了一个 BeanPostProcessor 的实现类 MergedBeanDefinitionPostProcessor,通过实现它的它的postProcessMergedBeanDefinition方法,可以对合并后的 RootBeanDefinition 进行一些处理。这里值得注意的是它被调用的时机,在这里修改 RootBeanDefinition 的时候,最初始的 Bean 实例已经被创建出来了,但是还没有进行后续的属性注入、初始化等流程。

其实我在看这段代码的时候有点好奇,有什么工作是需要在这时候处理的,直到我好奇地看了一下它有哪些实现类:

从名称可以看出,它们大部分都跟注解有关,应该是在这里处理一些注解相关的逻辑。

将早期的单例添加到三级缓存中

接着往下看代码。

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

这里看到了一些熟悉的东西。在 Spring 获取 Bean 实例对象的时候,会首先从缓存中查找(参考:Spring 源码阅读 21:循环依赖和三级缓存 )。而这部分代码,就是将刚刚完成创建的最原始的 Bean 对象,放到缓存中的过程。

首先,会判断是不是要曝光早期的单例,判断条件是,当前的 Bean 是单例 Bean,并且容器允许 Bean 之间循环引用,以及当前的 Bean 正在创建中。此处,我们要创建的 Bean 是正在创建的状态,如果其他两个条件也没有问题的话,就会通过addSingletonFactory方法,将它放入三级缓存singletonFactories集合中。

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

因为singletonFactories中保存的并不是 Bean 实例本身,而是一个 ObjectFactory,因此这里传入的参数也是一个 ObjectFactory 函数,其中调用了getEarlyBeanReference方法。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
         }
      }
   }
   return exposedObject;
}

主要是在返回前,通过 SmartInstantiationAwareBeanPostProcessor 后处理器的getEarlyBeanReference方法对 Bean 实例进行了处理,方法的默认实现是直接将传入的bean对象返回。如果需要在早期 Bean 实例被获取之前提前执行一些其他的准备工作,则可以在这里处理。

填充 Bean 实例

回到doCreateBean方法接着往后看。

// Initialize the bean instance.
Object exposedObject = bean;
try {
   populateBean(beanName, mbd, instanceWrapper);
   exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
   if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
      throw (BeanCreationException) ex;
   }
   else {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
   }
}

根据注释可以看到,这里进入了初始化的流程,其中有两个重要的方法,populateBeaninitializeBean。从方法名可以看出,它们分别负责 Bean 实例的填充和初始化。

我们先看populateBean方法。

又是一段比较长的逻辑。第一步先进行了判断,确保通过参数传入的 RootBeanDefinition 和 BeanWrapper 没有问题。

InstantiationAwareBeanPostProcessor 后处理

接下来又执行了一段后处理器的逻辑:

// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
            return;
         }
      }
   }
}

这里执行的是 InstantiationAwareBeanPostProcessor 的postProcessAfterInstantiation处理方法。

在之前的代码分析中,Spring 在没有使用默认的逻辑创建 Bean 之前,曾经调用了 InstantiationAwareBeanPostProcessor 的postProcessBeforeInstantiation方法,这里提供了一个可以自定义 Bean 实例创建逻辑的扩展点。

此处执行的postProcessAfterInstantiation方法返回值是boolean类型,默认返回true。考虑到这个方法的调用时机,是在早期的 Bean 实例创建之后,属性填充和初始化逻辑执行之前,Spring 允许 InstantiationAwareBeanPostProcessor 子类可以在 Bean 实例的属性被 Spring 自动装配之前执行一些属性注入的逻辑。

自动装配

接着看后面的代码。

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
   MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
   // Add property values based on autowire by name if applicable.
   if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
      autowireByName(beanName, mbd, bw, newPvs);
   }
   // Add property values based on autowire by type if applicable.
   if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      autowireByType(beanName, mbd, bw, newPvs);
   }
   pvs = newPvs;
}

这里获取了 BeanDefinition 中的propertyValues,也就是参数值的集合,供后续的流程使用。

然后调用了 BeanDefinition 的getResolvedAutowireMode来获取它的resolvedAutowireMode,我们进入方法体,看看是如何获取的:

// org.springframework.beans.factory.support.AbstractBeanDefinition#getResolvedAutowireMode
public int getResolvedAutowireMode() {
   if (this.autowireMode == AUTOWIRE_AUTODETECT) {
      // Work out whether to apply setter autowiring or constructor autowiring.
      // If it has a no-arg constructor it's deemed to be setter autowiring,
      // otherwise we'll try constructor autowiring.
      Constructor<?>[] constructors = getBeanClass().getConstructors();
      for (Constructor<?> constructor : constructors) {
         if (constructor.getParameterCount() == 0) {
            return AUTOWIRE_BY_TYPE;
         }
      }
      return AUTOWIRE_CONSTRUCTOR;
   }
   else {
      return this.autowireMode;
   }
}

这里其实就是获取 BeanDefinition 的自动装配模式,找到产量定义的地方,可以看到 BeanDefinition 的自动装配模式一共有一下几种:

int AUTOWIRE_NO = 0;
int AUTOWIRE_BY_NAME = 1;
int AUTOWIRE_BY_TYPE = 2;
int AUTOWIRE_CONSTRUCTOR = 3;
@Deprecated
int AUTOWIRE_AUTODETECT = 4;

它们分别表示不自动装配、通过名称自动装配、通过类型自动装配、通过构造器自动装配、让 Spring 自动选择装配模式。其中,最后一个已经不推荐使用。同时,通过查看autowireMode属性的定义,发现其默认值是AUTOWIRE_NO

private int autowireMode = AUTOWIRE_NO;

这个属性对应 XML 配置中bean标签的autowire属性,如果没有配置,那么它的默认值是AUTOWIRE_NO,此时,Spring 不会对相应的 Bean 执行自动装配的工作。

其实获取 BeanDefinition 的自动装配模式时,只需要获取autowireMode成员变量的值就行了,之所以在getResolvedAutowireMode方法中有一段判断逻辑,是为了处理它的值时AUTOWIRE_AUTODETECT,也就是让 Spring 自动选择装配模式的情况。

getResolvedAutowireMode方法中可以看到,会根据当前 BeanDefinition 对应的类型,判断其是否包含无参构造方法,如果包含久才用按类型自动装配的模式,否则采用构造器自动装配的模式。

下面回到处理自动装配的逻辑代码中if语句块的部分。

if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
   MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
   // Add property values based on autowire by name if applicable.
   if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
      autowireByName(beanName, mbd, bw, newPvs);
   }
   // Add property values based on autowire by type if applicable.
   if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      autowireByType(beanName, mbd, bw, newPvs);
   }
   pvs = newPvs;
}

在这里判断,如果当前 BeanDefinition 的自动装配模式时AUTOWIRE_BY_NAME或者AUTOWIRE_BY_TYPE则进行处理。其他人三种模式中,AUTOWIRE_AUTODETECT模式已经被进行了转换 ,AUTOWIRE_NO模式不需要做处理,而AUTOWIRE_CONSTRUCTOR模式的注入在之前已经处理过了,因为其通过构造器注入,之前创建 Bean 实例的时候,就已经进行了处理。因此这里只需要处理这两种情况。

还有一点需要注意,在if语句块的开头,通过创建一个 MutablePropertyValues 类型的对象,将pvs对象中的内容进行了深度复制得到了newPvs。接下来的自动装配操作,会对newPvs进行操作,而不会影响现在的pvs对象中的内容。可以在 MutablePropertyValues 的构造方法中看到它的创建逻辑。

// org.springframework.beans.MutablePropertyValues#MutablePropertyValues(org.springframework.beans.PropertyValues)
public MutablePropertyValues(@Nullable PropertyValues original) {
   // We can optimize this because it's all new:
   // There is no replacement of existing property values.
   if (original != null) {
      PropertyValue[] pvs = original.getPropertyValues();
      this.propertyValueList = new ArrayList<>(pvs.length);
      for (PropertyValue pv : pvs) {
         this.propertyValueList.add(new PropertyValue(pv));
      }
   }
   else {
      this.propertyValueList = new ArrayList<>(0);
   }
}

之后,根据具体的自动装配模式,分别使用autowireByName和autowireByType两个方法执行了装配的过程。执行完之后,将newPvs赋值给了pvs,因此,在之后的流程中,pvs是已经经过自动装配处理的。

下面具体看一下两种自动装配的过程。

按名称自动装配

进入autowireByName方法。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireByName
protected void autowireByName(
      String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

   String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
   for (String propertyName : propertyNames) {
      if (containsBean(propertyName)) {
         Object bean = getBean(propertyName);
         pvs.add(propertyName, bean);
         registerDependentBean(propertyName, beanName);
         if (logger.isTraceEnabled()) {
            logger.trace("Added autowiring by name from bean name '" + beanName +
                  "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
         }
      }
      else {
         if (logger.isTraceEnabled()) {
            logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
                  "' by name: no matching bean found");
         }
      }
   }
}

首先会通过unsatisfiedNonSimpleProperties获取到接下来需要遍历的所有属性名,我们进入方法,看一下这里具体筛选的是哪些属性。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
   Set<String> result = new TreeSet<>();
   PropertyValues pvs = mbd.getPropertyValues();
   PropertyDescriptor[] pds = bw.getPropertyDescriptors();
   for (PropertyDescriptor pd : pds) {
      if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
            !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
         result.add(pd.getName());
      }
   }
   return StringUtils.toStringArray(result);
}

从代码中可以看到,这里的筛选条件有这几个:

  1. 有属性对应的 setter 方法
  2. 不在排除列表中
  3. 不在 BeanDefinition 的 PropertyValues 中已经存在
  4. 不是简单数据类型(基本数据类型、字符串、Void 等等,以及这些类型的数组)

其实就是筛选出一些对工厂中其他 Bean 实例的引用。

回到autowireByName方法中,筛选完成之后,进行遍历操作。操作的过程也比较简单:

  1. 首先确保工厂中注册了当前属性名对应的 BeanDefinition,否则会记录一条错误日志并结束当前属性名相关的操作,这条错误日志应该很多开发 Spring 项目的人都见过。
  2. 然后通过getBean方法获取到这个 Bean 实例
  3. 将属性名和对应的 Bean 实例添加到pvs中,这个pvs最终会成为 BeanDefinition 的属性值集合。
  4. 通过registerDependentBean在容器中注册属性名对应的 Bean 和当前 Bean 的依赖关系。

这里的registerDependentBean方法,之前的文章已经分析过。在创建 Bean 实例之前,Spring 会先初始化当前 Bean 依赖的 Bean 的实例,也通过registerDependentBean方法在容器中注册了他们的依赖关系,具体内容可以参考 Spring 源码阅读 23:创建 Bean 实例前的准备工作 中「初始化依赖 Bean」这一小节。

至此,按照名称来处理自动装配的过程就结束了。

按类型自动装配

接下来看通过类型进行自动装配的过程。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#autowireByType
protected void autowireByType(
      String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

   TypeConverter converter = getCustomTypeConverter();
   if (converter == null) {
      converter = bw;
   }

   Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
   String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
   for (String propertyName : propertyNames) {
      try {
         PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
         // Don't try autowiring by type for type Object: never makes sense,
         // even if it technically is a unsatisfied, non-simple property.
         if (Object.class != pd.getPropertyType()) {
            MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
            // Do not allow eager init for type matching in case of a prioritized post-processor.
            boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
            DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
            Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
            if (autowiredArgument != null) {
               pvs.add(propertyName, autowiredArgument);
            }
            for (String autowiredBeanName : autowiredBeanNames) {
               registerDependentBean(autowiredBeanName, beanName);
               if (logger.isTraceEnabled()) {
                  logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
                        propertyName + "' to bean named '" + autowiredBeanName + "'");
               }
            }
            autowiredBeanNames.clear();
         }
      }
      catch (BeansException ex) {
         throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
      }
   }
}

这里整体的逻辑跟按名称自动装配的逻辑相似,不同点是,按名称进行自动装配时,由于名称的唯一性,在容器中最多能找到一个与之匹配的 Bean 实例,但是按类型自动装配的时候,可能找到不止一个,因此,按类型自动装配会支持数组类型的属性装配,同时,当存在多个类型的 Bean,但被装配的属性不是数组时,还可以根据 Bean 的顺序和优先级选取。

方法中声明的autowiredBeanNames集合,就是用于通过一个属性存在多个装配 Bean 实例的情况,装配完之后,会循环遍历这些 Bean 实例的名称,通过registerDependentBean方法为它们注册依赖关系。

查找注入属性值对象的工作是在resolveDependency方法中完成的。其中的主要逻辑就是根据类型查找容器中的 Bean,或者 Bean 集合。

属性值的后处理

自动装配的过程,只是把需要进行自动装配的属性对应的值,也就是 Bean 实例找到,并添加到 BeanDefinition 的属性值集合中,接下来,还需要将这些值(包括之前就已经有的以及刚刚通过自动装配获取到的)注入到 Bean 实例相应的属性中。

接着看之后的处理逻辑:

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
   if (pvs == null) {
      pvs = mbd.getPropertyValues();
   }
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
         if (pvsToUse == null) {
            if (filteredPds == null) {
               filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
               return;
            }
         }
         pvs = pvsToUse;
      }
   }
}

首先会执行一些 InstantiationAwareBeanPostProcessor 中的处理操作,这已经是创建和初始化 Bean 实例的过程中,第三次见到这个后处理器,此处执行的是postProcessPropertiespostProcessPropertyValues处理方法。中间还调用了一个filterPropertyDescriptorsForDependencyCheck方法,下面分别来看它们的作用。

对于后处理器的处理方法,它们绝大多数情况下是一个扩展点,因此,主要看它们被调用的时机以及参数和返回值,从中可以推测出 Spring 留扩展点的意图。此时调用后处理器方法的时机是,Spring 创建了 Bean 的早期实例,添加到了三级缓存中,并且完成了属性的自动装配,但是还没有将属性值注入到 Bean 实例当中。

postProcessProperties方法中,可以得到装配完的属性值集合,因此这里可以对属性值集合进行注入前的操作。如果这一步处理之后,返回了空的 PropertyValues,那么,会执行另外两个方法。

先看filterPropertyDescriptorsForDependencyCheck方法。

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#filterPropertyDescriptorsForDependencyCheck(org.springframework.beans.BeanWrapper, boolean)
protected PropertyDescriptor[] filterPropertyDescriptorsForDependencyCheck(BeanWrapper bw, boolean cache) {
   PropertyDescriptor[] filtered = this.filteredPropertyDescriptorsCache.get(bw.getWrappedClass());
   if (filtered == null) {
      filtered = filterPropertyDescriptorsForDependencyCheck(bw);
      if (cache) {
         PropertyDescriptor[] existing =
               this.filteredPropertyDescriptorsCache.putIfAbsent(bw.getWrappedClass(), filtered);
         if (existing != null) {
            filtered = existing;
         }
      }
   }
   return filtered;
}

这里主要是为了获取一个注入时需要忽略的属性的列表,包括我们之前分析过的感知接口的方法对应的属性。

这个列表会作为参数传入之后调用的后处理器方法postProcessPropertyValues中,这个方法返回的 PropertyValues 将作为最终要注入的属性值列表。当然,如果此时返回的还是null,则直接return,跳过之后的属性注入的逻辑。

属性注入

处理完属性值之后,看最后的注入逻辑。

if (needsDepCheck) {
   if (filteredPds == null) {
      filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
   }
   checkDependencies(beanName, mbd, filteredPds, pvs);
}

if (pvs != null) {
   applyPropertyValues(beanName, mbd, bw, pvs);
}

首先进行必要的检查,然后在 PropertyValues 不为空的情况下,调用applyPropertyValues方法,这个方法的主要作用就是将属性值注入到 Bean 早期实例的每一个对应的属性中。

总结

至此,本文分析了从 Spring 创建完早期 Bean 实例,到完成属性注入的过程。其中包括了将早期实例添加到三级缓存和属性自动装配的过程。在doCreateBean方法中,执行完populateBean方法之后,Bean 实例还不是最终的状态,接下来还有初始化的过程,放到下一篇接着分析。