从源码角度理解Spring Bean 的属性填充
一、引言
在Spring框架中,一个Bean的完整生命周期涉及多个复杂阶段。其中,属性填充(Property Population)是连接实例化与初始化的关键桥梁。
为了让你更清晰地理解上下文,我们先回顾一下AbstractAutowireCapableBeanFactory#doCreateBean方法的核心逻辑。这是所有非懒加载单例Bean创建的必经之路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) { BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance();
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { }
return exposedObject; }
|
从这段代码可以看出,populateBean方法夹在“实例化”与“初始化”之间。这意味着当进入populateBean时,对象已经存在(通过反射调用了构造器),但对象内部的成员变量(如@Autowired、@Resource、<property>标签等标注的字段)还是空的。我们的任务就是把这些配置或注解中定义的值,准确地塞进这些空字段里。
2. 属性填充入口:populateBean 宏观俯瞰
populateBean是整个属性注入流程的总调度室。它的逻辑虽然复杂,但结构非常清晰,采用了多个前置处理 + 单一执行的模式。
我们直接切入AbstractAutowireCapableBeanFactory中的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
if (bw == null) { if (!pvs.isEmpty()) { throw new BeanCreationException(...); } else { return; } }
boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } }
if (!continueWithPropertyPopulation) { return; }
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); }
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); }
pvs = newPvs; }
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } }
if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); }
if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } }
|
2.1 宏观流程小结
- 前置拦截:通过
postProcessAfterInstantiation可以完全关闭自动属性填充。
- 自动装配:处理
byName和byType的原始Spring自动装配(基于setter)。
- 注解后置处理:这是最关键的步骤,处理
@Autowired等注解,将依赖对象注入。
- 应用属性:将解析后的
PropertyValues(包含转换后的值或引用)通过反射/设置器写入对象。
接下来,我们将逐一深入这些核心环节。
3. postProcessAfterInstantiation 扩展点
在正式开始填充属性之前,Spring给了开发者一个“一票否决权”。
1 2 3 4 5 6 7 8
| if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } }
|
逻辑解析:
- 触发时机:Bean对象已经被实例化出来,但还没设置任何属性。
- 接口作用:
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation返回boolean值。
- 返回
true(默认):Spring继续执行后续的属性填充。
- 返回
false:Spring认为该Bean的依赖注入已经被自定义逻辑处理完毕,将跳过整个populateBean的后续所有步骤(包括byName、byType、注解注入等)。
设计意图:这个扩展点非常强大,它允许开发者实现完全自定义的属性注入逻辑,或者在某些特殊条件下完全阻止Spring的自动注入。比如,你可以基于外部配置或动态代理来决定是否注入某些属性。
4. 传统自动装配:byName 与 byType 深度解析
在Spring Boot盛行的今天,@Autowired注解已经成为了依赖注入的主流,但理解byName和byType这两种传统的基于XML的自动装配模式,对于理解Spring的IoC思想演变仍然至关重要。
这段逻辑只有在RootBeanDefinition中明确设置了autowire属性为byName或byType时才会触发。
4.1 autowireByName:简单直接的名称匹配
autowireByName的逻辑非常直观:找出Bean中所有需要自动装配的属性(非简单类型且未被显式配置),然后拿着这个属性名去容器中找同名的Bean。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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); } } }
|
什么是“非简单属性”? Spring定义了一套“简单类型”列表,主要包括:String、Number及其子类、Boolean、Date、URI、URL、Locale、Class以及以上类型的数组。如果属性类型不属于这些简单类型,且没有被显式赋值,就会被视为“需要自动装配的非简单属性”
4.2 autowireByType:复杂精妙的类型匹配
相较于byName,byType要复杂得多,因为它需要处理类型匹配、泛型、以及潜在的多个候选Bean的歧义问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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); if (Object.class != pd.getPropertyType()) { MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); DependencyDescriptor desc = new DependencyDescriptor(methodParam, false); Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { registerDependentBean(autowiredBeanName, beanName); } autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(...); } } }
|
核心逻辑:resolveDependency 方法封装了整个依赖解析的核心算法,它内部会处理:
- 类型匹配:找到所有与属性类型匹配的Bean。
- 候选筛选:如果有多个Bean匹配,会根据
@Primary、@Priority或beanName与属性名的匹配度(byType退化为byName的特性)进行筛选。
- 依赖注入:最终确定唯一的候选者并返回其实例。
5. 注解驱动注入:AutowiredAnnotationBeanPostProcessor 的工作机制
如果说byName和byType是Spring 1.x时代的产物,那么@Autowired、@Value和@Inject则是现代Spring(2.5+)的事实标准。这些注解的处理并非在populateBean中硬编码,而是通过我们之前提到的扩展点2——InstantiationAwareBeanPostProcessor#postProcessProperties来实现的。
具体的实现类是AutowiredAnnotationBeanPostProcessor。
5.1 元数据解析:寻找注入点
在Bean实例化之前(更准确地说是doCreateBean中的applyMergedBeanDefinitionPostProcessors阶段),AutowiredAnnotationBeanPostProcessor会作为一个MergedBeanDefinitionPostProcessor,提前解析出Bean中所有带有@Autowired、@Value等注解的字段和方法,并将其封装成InjectionMetadata对象缓存起来。这样做是为了避免在每次注入时都重复进行昂贵的反射解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); }
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz;
do { final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } });
ReflectionUtils.doWithLocalMethods(targetClass, method -> { });
elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements); }
|
5.2 运行时注入:从缓存到实例
当执行到populateBean中的postProcessProperties时,AutowiredAnnotationBeanPostProcessor直接利用之前缓存的InjectionMetadata信息进行注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Collection<InjectedElement> checkedElements = this.checkedElements; Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { element.inject(target, beanName, pvs); } } }
|
关键点:metadata.inject内部会针对每个AutowiredFieldElement或AutowiredMethodElement,调用resolveDependency解析出依赖值,然后通过ReflectionUtils.makeAccessible和Field.set暴力设置值,或者通过Method.invoke调用setter方法
6. 最后一击:applyPropertyValues 与类型转换
经历了上述阶段,所有需要注入的属性值(包括来自XML的pvs、byName/byType解析出的值、@Autowired直接注入的值)都汇集到了PropertyValues pvs这个对象中。但此时,这些值还“游离”于Bean对象之外。applyPropertyValues的作用,就是将这些值真正“塞进”对象里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { if (pvs.isEmpty()) { return; }
MutablePropertyValues mpvs = null; List<PropertyValue> original;
if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; if (mpvs.isConverted()) { try { bw.setPropertyValues(mpvs); return; } catch (BeansException ex) { throw new BeanCreationException(...); } } original = mpvs.getPropertyValueList(); } else { original = Arrays.asList(pvs.getPropertyValues()); }
TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; }
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
List<PropertyValue> deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; for (PropertyValue pv : original) { String propertyName = pv.getName(); Object originalValue = pv.getValue();
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { convertedValue = convertIfNecessary(propertyName, propertyValue, resolvedValue, bw, converter); } if (resolvedValue != originalValue || convertedValue != resolvedValue) { resolveNecessary = true; pv = new PropertyValue(pv, convertedValue); } deepCopy.add(pv); }
if (mpvs != null && !resolveNecessary) { mpvs.setConverted(); } else { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } }
|
6.1 BeanDefinitionValueResolver:值解析器
resolveValueIfNecessary方法负责处理各种特殊的“值”类型:
- RuntimeBeanReference:如果值是
<ref bean="xxx">,它会调用getBean("xxx")来获取实际的Bean实例。
- RuntimeBeanNameReference:解析为Bean的名称字符串。
- ObjectFactory:创建ObjectFactory对象。
- ManagedArray/List/Set/Map:解析集合中的每个元素(元素可能又是RuntimeBeanReference)。
- TypedStringValue:将字符串包装类中的字符串值提取出来,准备进行类型转换。
6.2 类型转换:TypeConverter 的魔力
Spring配置文件(XML或Properties)中的值本质上都是字符串。但Java对象的属性可能是Date、Integer、Boolean甚至自定义对象。
convertIfNecessary方法利用Spring的类型转换体系(TypeConverter + PropertyEditor + ConversionService)将这些字符串转换成目标类型。
例如,<property name="age" value="20"/>会被从String转换为int。
7. 循环依赖的“救命稻草”:三级缓存与提前曝光
理解属性填充,就绕不开循环依赖。Spring解决setter注入的单例Bean循环依赖,正是在属性填充阶段实现的。
回顾doCreateBean开头的代码:
1 2 3 4 5 6
| boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
|
假设我们有两个类A和B,相互引用。
- A的创建过程:
- A通过构造器实例化(对象A-raw)。
- A将
getEarlyBeanReference工厂放入三级缓存(singletonFactories)。
- 开始填充A的属性,发现需要
B b。
- B的创建过程:
- 容器去创建B。
- B通过构造器实例化(对象B-raw)。
- B将自身的工厂放入三级缓存。
- 开始填充B的属性,发现需要
A a。
- 关键点:B去调用
getBean("a")获取A的依赖。在getSingleton方法中,一级缓存没有,二级缓存没有,但在三级缓存中找到了A的工厂。
- 执行工厂的
getObject()方法(即getEarlyBeanReference),获取A的早期引用(可能是一个AOP代理,也可能是原始对象A-raw)。这个早期引用被放入二级缓存(earlySingletonObjects),并从三级缓存移除。
- B成功获取到A的引用(虽然此时的A还没有完成属性填充和初始化,但引用是有效的)。
- B继续完成自己的属性填充和初始化,将自己作为一个成品放入一级缓存。
- A的完成:
- B创建完成后,A回到自己的属性填充阶段,成功从一级缓存(或二级缓存)中获取到已完成的B对象。
- A完成自己的属性填充和初始化,成为成品放入一级缓存。
结论:正是populateBean阶段对依赖的获取,触发了三级缓存的调用,从而打破了死循环。如果没有这个“在属性填充前去三级缓存曝光自己”的机制,构造器注入的循环依赖就永远无法解开。
8. 深入源码的角落:其他机制解析
8.1 依赖检查
在populateBean中,checkDependencies用于验证Bean所需的所有依赖(简单类型或对象类型)是否都已填充。如果配置为DEPENDENCY_CHECK_OBJECTS,但某个对象类型的属性为null,就会抛出异常。这在现代Spring开发中已很少使用,通常由@Autowired(required=true)替代。
8.2 Record类的支持
Spring 5.1+(特别是Spring 6/Spring Boot 3)开始支持Java Record类。Record是不可变数据类,没有setter方法。在populateBean开头有一段特殊逻辑:
java
1 2 3 4 5 6
| if (bw.getWrappedClass().isRecord()) { if (mbd.hasPropertyValues()) { throw new BeanCreationException(...); } return; }
|
这表示Spring会跳过对Record类的属性填充。Record的初始化完全依赖于构造器完成。
9. 总结:一张时序图与设计思想
9.1 核心流程回顾
- 入口:
populateBean被doCreateBean调用。
- 否决权:
postProcessAfterInstantiation决定是否继续。
- 传统装配:处理
byName/byType,解析依赖放入PropertyValues。
- 注解注入:
AutowiredAnnotationBeanPostProcessor执行@Autowired注入。
- 依赖检查:校验必须的属性是否存在。
- 值解析与应用:
applyPropertyValues处理引用、集合、类型转换,最终通过BeanWrapper写入目标Bean。
9.2 设计思想提炼
- 分层处理,职责单一:
populateBean只负责流程编排,具体的依赖解析(resolveDependency)、值处理(BeanDefinitionValueResolver)、类型转换(TypeConverter)均由专门的组件完成。
- 开放闭合原则(OCP):通过
InstantiationAwareBeanPostProcessor,Spring允许开发者无侵入地扩展属性填充的逻辑(如添加新的注解@Inject)。
- 策略模式:
byName和byType是两种不同的自动装配策略,通过条件判断进行切换。
- 缓存与性能:
AutowiredAnnotationBeanPostProcessor缓存注入点元数据;BeanWrapper缓存内省结果;三级缓存解决循环依赖。处处体现了对性能的考量。