Spring 源码(16)Spring Bean的创建过程(7)属性填充
知识回顾
上一篇介绍了Spring
中三级缓存的singletonObjects
、earlySingletonObjects
、singletonFactories
,Spring
在处理循环依赖时在实例化后属性填充前将一个lambda
表达式放在了三级缓存中,后续在获取时进行了判断,如果不需要进行对象代理,那么直接返回对象Bean
,然后将三级缓存中的对象删除,然后放在二级缓存中,后面在初始化之后又将二级缓存中的对象放在了一级缓存中,然后删除了二级缓存中的对象。
然后介绍了Spring
在进行代理对象的创建时,会使用SmartInstantiationBeanPostProcessor
接口的getEarlyBeanReference
方法进行创建,创建的时候会调用到AbstractAutoProxyCreator
类的实现,最终以JDK
或者CGLIB
的方式进行代理的创建,当然这些细节讲的不是很清晰,只是梳理了大致脉络,后续还会进行较为详细的梳理,敬请期待。
接下来继续主流程的解析,Bean
的属性填充。
对象的属性填充
一般来说属性填充,可能会涉及到很多东西,比如填充的属性是基本类型还是引用类型,填充的方式又可以分为按类型、按名称还是其他的,然后填充时值的类型是否需要进行类型转换等。
属性填充:
属性填充大致可以分为对基本类型的数据进行填充和对应用类型的数据填充,populateBean
方法中代码比较繁琐,会设计到很多的递归调用,最终解析并填充属性。
在Spring
中,实际上属性填充大致可以分为四步:
- 使用
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
接口的调用,可以给机会在属性填充前对Bean
进行修改,并且可以定制字段的填充 - 按照注入方式进行属性的填充,最终会将解析到的属性和引用放入
PropertyValues
中- 按类型进行自动注入
- 按名称进行自动注入
- 如果存在
InstantiationAwareBeanPostProcessor
接口,那么循环去调用postProcessProperties
这个方法进行注解的注入,这里调用的实际上就是前面进行Bean
的合并时解析的注解,比如:@Autowired
、@Resource
、@Value
等 - 属性值的处理和解析
- 创建一个
BeanDefinitionValueResolver
值解析器 - 循环去遍历解析属性值,解析过程中会用到类型转换的转换服务
ConversionService
、SPEL
表达式的解析、属性编辑器PropertyEditor
- 最终解析完,会调用到属性的
set
方法进行写入
- 创建一个
这里贴一下属性值解析的时候的源码:
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// We must check each value to see whether it requires a runtime reference
// to another bean to be resolved.
// 根据值对象的类型进行解析
if (value instanceof RuntimeBeanReference) {
// 运行时引用
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
else if (value instanceof RuntimeBeanNameReference) {
String refName = ((RuntimeBeanNameReference) value).getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (!this.beanFactory.containsBean(refName)) {
throw new BeanDefinitionStoreException(
"Invalid bean name '" + refName + "' in bean reference for " + argName);
}
return refName;
}
else if (value instanceof BeanDefinitionHolder) {
// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
else if (value instanceof BeanDefinition) {
// Resolve plain BeanDefinition, without contained name: use dummy name.
BeanDefinition bd = (BeanDefinition) value;
String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
ObjectUtils.getIdentityHexString(bd);
return resolveInnerBean(argName, innerBeanName, bd);
}
else if (value instanceof DependencyDescriptor) {
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
Object result = this.beanFactory.resolveDependency(
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
for (String autowiredBeanName : autowiredBeanNames) {
if (this.beanFactory.containsBean(autowiredBeanName)) {
this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);
}
}
return result;
}
// 如果值是数组
else if (value instanceof ManagedArray) {
// May need to resolve contained runtime references.
ManagedArray array = (ManagedArray) value;
Class<?> elementType = array.resolvedElementType;
if (elementType == null) {
String elementTypeName = array.getElementTypeName();
if (StringUtils.hasText(elementTypeName)) {
try {
elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
array.resolvedElementType = elementType;
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error resolving array type for " + argName, ex);
}
}
else {
elementType = Object.class;
}
}
return resolveManagedArray(argName, (List<?>) value, elementType);
}
// 如果值是list
else if (value instanceof ManagedList) {
// May need to resolve contained runtime references.
return resolveManagedList(argName, (List<?>) value);
}
// 如果值是set
else if (value instanceof ManagedSet) {
// May need to resolve contained runtime references.
return resolveManagedSet(argName, (Set<?>) value);
}
// 如果值是map
else if (value instanceof ManagedMap) {
// May need to resolve contained runtime references.
return resolveManagedMap(argName, (Map<?, ?>) value);
}
// 如果值是properties
else if (value instanceof ManagedProperties) {
Properties original = (Properties) value;
Properties copy = new Properties();
original.forEach((propKey, propValue) -> {
if (propKey instanceof TypedStringValue) {
propKey = evaluate((TypedStringValue) propKey);
}
if (propValue instanceof TypedStringValue) {
propValue = evaluate((TypedStringValue) propValue);
}
if (propKey == null || propValue == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error converting Properties key/value pair for " + argName + ": resolved to null");
}
copy.put(propKey, propValue);
});
return copy;
}
// 如果值是字符串
else if (value instanceof TypedStringValue) {
// Convert value to target type here.
TypedStringValue typedStringValue = (TypedStringValue) value;
// 校验值,比如说使用spel表达式进行解析,然后得到这个值
Object valueObject = evaluate(typedStringValue);
try {
Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) {
// 类型转换
return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
}
else {
return valueObject;
}
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error converting typed String value for " + argName, ex);
}
}
else if (value instanceof NullBean) {
return null;
}
else {
return evaluate(value);
}
}
代码比较长,大致的话就是对属性进行分类处理,比如引用类型的,List
、Map
、Set
、Properties
、数组、String
类型的等。
如果在填充的过程中,发现需要的Bean
不存在,那么又会进行getBean
、doGetBean
、createBean
、doCreateBean
的调用,然后递归的入栈出栈调用,最终完成属性的填充。
下篇继续主流程中的Bean
的初始化initializeBean
调用的解读。
Spring 源码(16)Spring Bean的创建过程(7)属性填充的更多相关文章
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- Spring源码-IOC部分-Bean实例化过程【5】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- dubbo源码分析3-service bean的创建与发布
dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...
- Spring源码分析专题 —— IOC容器启动过程(上篇)
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
- 【Spring源码分析】Bean加载流程概览(转)
转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...
- Spring源码分析:Bean加载流程概览及配置文件读取
很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- 初探Spring源码之Spring Bean的生命周期
写在前面的话: 学无止境,写博客纯粹是一种乐趣而已,把自己理解的东西分享出去,不意味全是对的,欢迎指正! Spring 容器初始化过程做了什么? AnnotationConfigApplication ...
随机推荐
- C++重载输入流、输出流运算符
在c++中类的私有成员是不能被直接访问的,需要通过类中提供的成员函数简介的操作这些数据.同时C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型 ...
- 5_系统的可控性_Controllability
- Solution Architect
Solution Architect How to become a solution architect - CareerExplorer What does a solution architec ...
- 微信小程序 使用filter过滤器几种方式
由于微信小程序 技术生态比较闭合,导致很多 现代前端框架很多积累出的成果都没有实现(可能未来会逐一实现). 用惯了现代 再耍小程序 总感觉很不顺手. 需要结果的请直接看最后的WXS View Filt ...
- Html5 Canvas学习之路(五)
Canvas 图像(上) Canvas 图像API可以加载图像数据,然后直接将图像应用到画布上.还可以裁切.拼贴图像数据,以显示用户需要的部分.此外,Canvas还提供了像素数据的存储功能,这样就能对 ...
- 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte
问题描述:在使用python爬取斗鱼直播的数据时,使用str(读取到的字节,编码格式)进行解码时报错:'utf-8' codec can't decode byte 0x8b in position ...
- PAT B1031查验身份证
一个合法的身份证号码由17位地区.日期编号和顺序编号加1位校验码组成.校验码的计算规则如下: 首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8, ...
- 关于#pragma 和 _pragma
首先要明确 #pragma 和_Pragma 是什么 这两个都是出自于c/c++ 的 ,其中#pragma 是预处理指令(preProcess directive ) ,#pragma是用来向编译器传 ...
- 接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理)
接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理) 1.抽象类 以下说辞可能不太准确,但是会让你醍醐灌顶 抽象类是把一些具有共同属性(包括行为)的东西抽象出来,比如: 小狗有身高,体重,颜 ...
- c++对c的拓展_常量引用
常量引用:不能通过引用去修改引用所指向的内容 const int &ref =val; // const int *const ref =&val; 注意:可引用常量 (普通引用无法 ...