携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第35天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
前情提要
前两篇,从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);
}
}
根据注释可以看到,这里进入了初始化的流程,其中有两个重要的方法,populateBean
和initializeBean
。从方法名可以看出,它们分别负责 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);
}
从代码中可以看到,这里的筛选条件有这几个:
- 有属性对应的 setter 方法
- 不在排除列表中
- 不在 BeanDefinition 的 PropertyValues 中已经存在
- 不是简单数据类型(基本数据类型、字符串、Void 等等,以及这些类型的数组)
其实就是筛选出一些对工厂中其他 Bean 实例的引用。
回到autowireByName
方法中,筛选完成之后,进行遍历操作。操作的过程也比较简单:
- 首先确保工厂中注册了当前属性名对应的 BeanDefinition,否则会记录一条错误日志并结束当前属性名相关的操作,这条错误日志应该很多开发 Spring 项目的人都见过。
- 然后通过getBean方法获取到这个 Bean 实例
- 将属性名和对应的 Bean 实例添加到
pvs
中,这个pvs
最终会成为 BeanDefinition 的属性值集合。 - 通过
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 实例的过程中,第三次见到这个后处理器,此处执行的是postProcessProperties
和postProcessPropertyValues
处理方法。中间还调用了一个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 实例还不是最终的状态,接下来还有初始化的过程,放到下一篇接着分析。