spring源码干货分享-对象创建详细解析(set注入和初始化)
记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新。欢迎大家指正!
环境: spring5.X + idea
建议:学习过程中要开着源码一步一步过
Spring根据BeanDefinition创建单例对象
DefaultSingletonBeanRegister中getSingleton((String beanName, ObjectFactory<?> singletonFactory))
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName, ...);
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
解析:
- getSingleton 方法中还是先要在单例池中获取将要被创建的对象是不是存在,不存在了才去创建。this.singletonObjects.get(beanName); 如果不存在Spring还会效验一下是不是正在被销毁,是则抛异常。this.singletonsCurrentlyInDestruction
- 一系列效验完成后开始正式创建对象的第一步
beforeSingletonCreation(beanName);
创建对象前要满足俩个条件inCreationCheckExclusions&&singletonsCurrentlyInCreation.add(beanName),当前这个Bean没有被排除,并且这个bean正在创建中。 - 通过
singletonFactory.getObject();回调createBean(beanName, mbd, args);
创建对象时先获取对应的BeanClass(通过反射创建对象),调用doCreateBean(beanName, mbdToUse, args);,这个方法中才是正儿八经的实例化对象
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
//....
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
//.............
// 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);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
doCreateBean解析:
A. 创建对象:createBeanInstance(beanName, mbd, args);
- 通过工厂方法创建对象
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
- 通过默认的无参构造创建对象
instantiateBean(beanName, mbd);
B. 填充属性:populateBean(beanName, mbd, instanceWrapper); 也是就是Set注入(构造注入已经在创建对象时执行了,自动注入可以忽略)
- 注解属性赋值
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;
}
}
}
解析:通过注解对属性填充使用了AutowiredAnnotationBeanPostProcessor实现所有操作,在postProcessProperties(pvs, bw.getWrappedInstance(), beanName);方法中真正实现。
- 标签方式属性赋值
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
解析:从RootBeanDefinition中获取属性的List封装到PropertyValues中。
遍历ProperValues 把每个属性的值进行类型转换,最后再给bw设置得到的属性值。-> BeanWrapper.setProperValues(new MutablePropertyValues(deepCopy))。
自定义类型会通过beanFactory.getBean() 获取对应的对象,再走一遍doGetBean() -> createBean() -> doCreateBean()...
(JDK的类型会先封装TypeStringValues。自定义的类型会封装RuntimeBeanReference)
C. 初始化对象:initializeBean(beanName, exposedObject, mbd);
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
.......
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
......
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
解析:初始化分几个功能
1. Aware -> invokeAwareMethods(String beanName, Object bean)。spring提供的帮助我们获取Spring容器中对应的组件的相关信息。
2. BeanPostProcessorsBefore -> applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);已经创建好的对象初始化之前再次加工。
3. 初始化 -> invokeInitMethods(beanName, wrappedBean, mbd);包含了接口初始化((InitializingBean) bean).afterPropertiesSet();和自定义方法初始化invokeCustomInitMethod(beanName, bean, mbd);
4. BeanPostProcessorsAfter -> applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);已经创建好的对象初始化之后再次加工。
最后
感谢您的阅读,有什么意见和问题欢迎评论区留言!书写不易!
觉得文章对你有帮助记得给我点个赞,欢迎大家关注和转发文章!
spring源码干货分享-对象创建详细解析(set注入和初始化)的更多相关文章
- Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器
承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ...
- Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器
本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析 spring配置文件应用 <context: ...
- Spring源码情操陶冶-自定义节点的解析
本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...
- Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器
本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...
- Spring框架源码干货分享之三级缓存和父子工厂
记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新.欢迎大家指正! 环境: spring5.X + idea 建议:学习过程中要开着源码一步一步过 Spring中对象的创建宏观 ...
- 【spring源码系列】之【xml解析】
1. 读源码的方法 java程序员都知道读源码的重要性,尤其是spring的源码,代码设计不仅优雅,而且功能越来越强大,几乎可以与很多开源框架整合,让应用更易于专注业务领域开发.但是能把spring的 ...
- spring源码学习之默认标签的解析(一)
继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...
- Spring源码分析(十)注册解析的BeanDefinition
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 对配置文件解析完成后,获取的beanDefiniton已经可以进行使用了 ...
- Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器
aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...
随机推荐
- UIView属性的讲解
1.父控件和子控件的理解在storyboard中只有UIView是可以在里面拖入子控件的,其他控件不可以(必须通过代码添加)拖入一个UIView控件,在里面添加一些子控件(UIView控件是控制器的V ...
- SQL的多表查询(笛卡尔积原理)
感谢大佬:https://blog.csdn.net/yang5726685/article/details/53538438 MySQL的多表查询(笛卡尔积原理) 先确定数据要用到哪些表. 将多个表 ...
- mac版mysql初次密码不知道或以后忘记密码重设密码步骤
我自己装完MySQL 不知道怎么回事,初始密码就是登陆不了,幸好找到了这个,严格按照步骤就行了, 完全可以复制粘贴 这个是在百度贴吧看到的作者 贴吧id叁寸日光_1987 苹果->系统偏好设置- ...
- java+selenium脚本编写规范
2. 源文件规范 2.1 文件名 源文件以最顶层的类名来命名,大小写敏感,文件扩展名为.java 2.2 文件编码 UTF-8 源文件要求编码格式为UTF-8 2.3 源文件结 ...
- [USACO4.2]工序安排Job Processing
两种想法: (样例是真的良心,卡掉了两种错误做法)洗完一件马上塞一件到最快的空闲烘干机去?X,因为最后一件洗完的衣服决定了第二问的答案,但它并不一定得到最优待遇--最快的烘干机. 给最后一件洗完的 ...
- visual studio自动向量化
//////////////////////////////////////////////////*SSE 和 AVX 每个都有16个寄存器SSE 有 XMM0 ~ XMM15,是128bitAVX ...
- Solution -「CF 1391E」Pairs of Pairs
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的无向图,在其上找到一条包括不少于 \(\lceil\frac{n}2\rceil\ ...
- Linux海王 之 pdsh (并行管理工具)
文章目录 安装 使用 示例 -w 指定主机 -l 指定用户 -g指定用户组 主机列表 交互式界面 pdsh是一个多线程远程shell客户机,它在多个远程主机上并行执行命令 pdsh可以使用几种不同的远 ...
- JMM之Java中锁概念的分类总结
在Java的并发编程中不可避免的涉及到锁.从不同维护可以将锁进行不同的分类,如下: 1.乐观锁和悲观锁(根据读写操作的比例划分) 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数 ...
- CentOS8系统上Zookeeper安装启动
下载Zookeeper安装包,下载地址为:https://zookeeper.apache.org/releases.html.注意选择bin的gz包,否则安装不成功,提示找不到相应的类. 一.单机模 ...