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 ...
随机推荐
- TL431常用电路整理
熟悉电路制作的人大多对TL431并不陌生.由于TL431的动态抗阻的特性,其经常在电路设计当中被用于替代稳压二极管.不仅如此,TL431的开态响应速度快输出噪音低,并且价格低廉.因此受到电源工程师和初 ...
- RESTful API/Web API
Microsoft REST API Guidelines Are Not RESTful White House Web API Standards Microsoft REST API Guide ...
- ES6-11学习笔记--const
新声明方式:const 1.不属于顶层对象 window 2.不允许重复声明 3.不存在变量提升 4.暂时性死区 5.块级作用域 以上特性跟let声明一样,特性可看 let 的学习笔记:链接跳转 ...
- web开发者踏入人工智能的利器_Tensorflow.js
前言 最近公司向员工搜集公司杂志的文章,刚好最近学习了机器学习相关课程.为了赚取购买课程的费用,所以写了如下文章投稿赚取稿费. 如下文章可能涉及一些我所购买课程的内容,所以不便将所有资源进行展示. 当 ...
- C#设计编写一个控制台应用程序
设计编写一个控制台应用程序,练习类的继承. (1) 编写一个抽象类 People,具有"姓名","年龄"字段,"姓名"属性,Work 方法. ...
- 人机交互大作业---C#WinForm酒店预订系统(纯界面)
登录: 所有界面: 源代码:最近较忙,后续会上传至github 材料参考:扬中菲尔斯金陵大酒店
- 一个动态波浪纹Android界面
IndexActivity.java package com.example.rubikrobot; import androidx.appcompat.app.AppCompatActivity; ...
- Qt QTableView 表格内添加控件
目录 Qt QTableView 表格内添加控件 1. QItemDelegate 2. setIndexWidget 3. setIndexWidget + setLayout Qt QTableV ...
- c++对c的拓展_引用的基本用法
实质:取别名 格式:原类型&别名=原变量名: 注意:1.定义时必须初始化 2.初始化后不能够改变指向 3.不可对Null进行引用 4.可对任意类型取别名包括数组(int (&别名)[个 ...
- JavaSE常用类之File类
File类 只用于表示文件或目录的信息,不能对文件内容进行访问. java.io.File类∶代表文件和目录.在开发中,读取文件.生成文件.删除文件.修改文件的属性时经常会用到本类. File类不能访 ...