前言

本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。

本篇文章主要介绍 Spring IoC 容器中 bean 的属性赋值阶段。

正文

我们在Spring IoC bean 的创建一文中分析创建 bean 实例的主要流程,此时创建出来的 bean 还是个属性未赋值的实例,在创建完之后会进入 populateBean() 方法,即进入属性赋值阶段。我们简单回顾一下,上次分析过的 doCreateBean() 方法:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

    // 实例化 bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 如果bean的作用域是singleton,则需要移除未完成的FactoryBean实例的缓存
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 通过构造函数反射创建bean的实例,但是属性并未赋值,见下文详解
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获取bean的实例
final Object bean = instanceWrapper.getWrappedInstance();
// 获取bean的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
} synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// BeanDefinition 合并后的回调,见下文详解
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
// 省略异常处理...
mbd.postProcessed = true;
}
} // bean的作用域是单例 && 允许循环引用 && 当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
// 如果允许bean提前曝光
if (earlySingletonExposure) {
// 将beanName和ObjectFactory形成的key-value对放入singletonFactories缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} Object exposedObject = bean;
try {
// 给 bean 的属性赋值
populateBean(beanName, mbd, instanceWrapper);
// 初始化 bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// 省略部分代码
}

属性赋值

AbstractAutowireCapableBeanFactory#populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
} else {
return;
}
} // 给InstantiationAwareBeanPostProcessors最后一次机会在属性设置前来改变bean
// 例如:可以用来支持属性注入的类型
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
// 这里会调用bean实例化后的生命周期回调,返回false会跳过下面的属性赋值阶段
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
// 获取PropertyValues
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// 获取依赖注入类型
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
// 如果依赖注入类型是 byName 或者 byType
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// 赋值pvs到可修改的MutablePropertyValues
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 根据名称自动注入,见下文详解
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// 根据类型自动注入,见下文详解
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
} // 是否有注册InstantiationAwareBeanPostProcessors的实现类
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
// 遍历并找到InstantiationAwareBeanPostProcessor的实现类,调用处理属性值的后置处理方法
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);
// 如果属性值的后置处理方法返回null,直接返回,不会进行底下的属性值应用阶段
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);
}
}

上面方法首先会调用 bean 的实例化后生命周期回调方法,如果返回 false 会跳过下面的属性赋值阶段。关于 InstantiationAwareBeanPostProcessors 接口在Spring IoC bean 的创建一文中介绍过,这里不再赘述。接着判断是否是按 名称 或者 类型 自动注入属性并填入 newPvs 中,接着调用 bean 属性填充前的生命周期回调。属性填充前生命周期回调方法有两个 postProcessProperties()postProcessPropertyValues(),第一个是 Spring 5.1 新加的,后面的是老的,已经被标记为过时;首先会调用 postProcessProperties() 如果返回空调用 postProcessPropertyValues(),否则直接使用返回的 PropertyValuespostProcessPropertyValues() 如果返回空会直接跳过属性填充阶段,不为空直接使用返回的 PropertyValues

按照名称依赖注入

AbstractAutowireCapableBeanFactory#autowireByName

protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 寻找bw中需要依赖注入的属性名称
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
// 遍历需要注入的bean
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
// 调用getBean()方法获取bean
Object bean = getBean(propertyName);
// 将需要注入bean的实例加入到pvs
pvs.add(propertyName, bean);
// 注册依赖关系
registerDependentBean(propertyName, beanName);
}
}
}

上面的方法很简单,就是寻找 bean 的非简单类型并且不存在于 mbd.getPropertyValues() 中的属性,然后遍历调用 getBean() 方法去获取实例,完成注入。

非简单类型就是指除去8个原始类型、String类型、Number类型、Date类型、URL类型、URI类型的其它类型。

registerDependentBean() 方法在Spring IoC bean 的加载一文中有分析过,这里不再赘述。

按照类型依赖注入

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);
// 获取bean中非简单属性
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// 根据类型注入永远不要注入Object类型,你细细地品一下
if (Object.class != pd.getPropertyType()) {
// 获取属性的可写方法,一般是set方法
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
// 依赖解决,最后返回符合条件需要注入的bean实例
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
// 需要注入的bean实例不为空,加入到pvc
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
// 注册依赖关系
registerDependentBean(autowiredBeanName, beanName);
}
autowiredBeanNames.clear();
}
} catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}

上面方法中的 resolveDependency() 方法在Spring IoC bean 的创建一文中介绍过,这里不再赘述。

属性赋值

AbstractAutowireCapableBeanFactory#applyPropertyValues

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// 属性为空,直接返回
if (pvs.isEmpty()) {
return;
} if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
} MutablePropertyValues mpvs = null;
List<PropertyValue> original; if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 快捷方式,如果属性已经转换过,直接填充进BeanWrapper
if (mpvs.isConverted()) {
try {
bw.setPropertyValues(mpvs);
return;
} catch (BeansException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
// 属性没有转换过,获取属性列表
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) {
// 如果pv类型转换过,直接添加进deepCopy
if (pv.isConverted()) {
deepCopy.add(pv);
} else {
// 进行转换
// 拿到pv原始属性名和属性值
String propertyName = pv.getName();
Object originalValue = pv.getValue();
if (originalValue == AutowiredPropertyMarker.INSTANCE) {
Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
if (writeMethod == null) {
throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
}
originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
}
// 进行类型转换
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
// 如果可转换,则转换指定目标属性的给定值
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// 在合并的BeanDefinition中存储转换后的值,以避免为每个创建的bean实例重新转换
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
} else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
} else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
} try {
// 填充bean属性值
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
} catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}

上面代码中的 bw.setPropertyValues() 方法最终会调用 BeanWrapperImpl#setVlaue() 方法,如下:

public void setValue(final @Nullable Object value) throws Exception {
// 这里一般就是set方法
final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor)this.pd).getWriteMethodForActualAccess() : this.pd.getWriteMethod());
// 利用反射调用set方法给属性赋值
ReflectionUtils.makeAccessible(writeMethod);
writeMethod.invoke(getWrappedInstance(), value); }

下图是我 debug 时的截图,可以看到基本上就是在调用属性的 setter 方法:

注意:没有 setter 方法时会抛出异常。

总结

本篇文章主要分析了 Spring IoC 的属性赋值阶段的流程,Spring 在此阶段也提供了2个扩展点;分别是 bean 的实例化后和属性赋值前,即 InstantiationAwareBeanPostProcessor 接口的 postProcessAfterInstantiation() 方法和 postProcessProperties() 方法。需要注意的是在 XML 中配置的 autowire 属性,不管是 byName 还是 byType 都需要 setter 方法,但是我们平时在使用 @Autowire 注解时并不需要 settter 方法,原因会在分析 @Autowire 注解时讲述。

Spring IoC 属性赋值阶段的更多相关文章

  1. Spring IoC @Autowired 注解详解

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 我们平时使用 Spring 时,想要 依赖 ...

  2. 死磕Spring之IoC篇 - 深入了解Spring IoC(面试题)

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...

  3. Spring IoC createBean 方法详解

    前言 本篇文章主要分析 Spring IoC 的 createBean() 方法的流程,以及 bean 的生命周期. 下面是一个大致的流程图: 正文 AbstractAutowireCapableBe ...

  4. Spring IoC 循环依赖的处理

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 是 ...

  5. Spring IoC bean 的初始化

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...

  6. 【小家Spring】Spring IoC是如何使用BeanWrapper和Java内省结合起来给Bean属性赋值的

    #### 每篇一句 > 具备了技术深度,遇到问题可以快速定位并从根本上解决.有了技术深度之后,学习其它技术可以更快,再深入其它技术也就不会害怕 #### 相关阅读 [[小家Spring]聊聊Sp ...

  7. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  8. [原创]java WEB学习笔记98:Spring学习---Spring Bean配置及相关细节:如何在配置bean,Spring容器(BeanFactory,ApplicationContext),如何获取bean,属性赋值(属性注入,构造器注入),配置bean细节(字面值,包含特殊字符,引用bean,null值,集合属性list map propert),util 和p 命名空间

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

随机推荐

  1. Java实现 蓝桥杯VIP 算法提高 夺宝奇兵

    算法提高 夺宝奇兵 时间限制:1.0s 内存限制:512.0MB [题目描述] 在一座山上,有很多很多珠宝,它们散落在山底通往山顶的每条道路上,不同道路上的珠宝的数目也各不相同.下图为一张藏宝地图: ...

  2. lambda表达式操作DataTable

    using System;using System.Collections.Generic;using System.Data;using System.Linq;using System.Text; ...

  3. sql server 连接种类

    一.连接种类 内连接 inner join 如果分步骤理解的话,内连接可以看做先对两个表进行了交叉连接后,再通过加上限制条件(SQL中通过关键字on)剔除不符合条件的行的子集,得到的结果就是内连接了. ...

  4. yum安装配置MySQL数据库

    1.配置yum源 # 先安装wget yum install wget -y     2.下载mysql源安装包 wget http://dev.mysql.com/get/mysql57-commu ...

  5. Android拍照相关知识总结

    1.调用系统的照相机程序 Intent intent = newIntent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(inte ...

  6. sorted排序

    sorted(['bob', 'about', 'Zoo', 'Credit']) # ['Credit', 'Zoo', 'about', 'bob'] ''' 默认情况下,对字符串排序,是按照AS ...

  7. yaml读取封装

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ 对yaml格式的配置文件的操作 """ ...

  8. loadrunner安装负载机

    1,安装docker 2,下载最新版本的load_generator镜像,命令如下: docker pull hpsoftware/load_generator 3,load_generator镜像实 ...

  9. 技术周刊 · Lighthouse 测试报告生成

    登高远眺 天高地迥,觉宇宙之无穷 基础技术 Lighthouse 测试内幕 文章分享了网易云音乐前端性能监控平台使用 Lighthouse 的实践经验,介绍了 Lighthouse 的测试流程.内部模 ...

  10. VMware历史版本下载【1.0~3.0】

    前提:此为走HTTP协议的FTP伺服器,而且有直到Vmware3.0[之后就没了]的版本 link:http://linux.mathematik.tu-darmstadt.de/pub/linux/ ...