记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新。欢迎大家指正!

环境: spring5.X + idea

建议:学习过程中要开着源码一步一步过

Spring根据BeanDefinition创建单例对象

  1. 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;
}
}

解析:

  1. getSingleton 方法中还是先要在单例池中获取将要被创建的对象是不是存在,不存在了才去创建。this.singletonObjects.get(beanName); 如果不存在Spring还会效验一下是不是正在被销毁,是则抛异常。this.singletonsCurrentlyInDestruction
  2. 一系列效验完成后开始正式创建对象的第一步beforeSingletonCreation(beanName);

    创建对象前要满足俩个条件inCreationCheckExclusions&&singletonsCurrentlyInCreation.add(beanName),当前这个Bean没有被排除,并且这个bean正在创建中。
  3. 通过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);

  1. 通过工厂方法创建对象
  if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
  1. 通过默认的无参构造创建对象
  instantiateBean(beanName, mbd);

B. 填充属性:populateBean(beanName, mbd, instanceWrapper); 也是就是Set注入(构造注入已经在创建对象时执行了,自动注入可以忽略)

  1. 注解属性赋值
	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);方法中真正实现。

  1. 标签方式属性赋值
  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注入和初始化)的更多相关文章

  1. Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器

    承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ...

  2. Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器

    本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析 spring配置文件应用 <context: ...

  3. Spring源码情操陶冶-自定义节点的解析

    本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...

  4. Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器

    本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...

  5. Spring框架源码干货分享之三级缓存和父子工厂

    记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新.欢迎大家指正! 环境: spring5.X + idea 建议:学习过程中要开着源码一步一步过 Spring中对象的创建宏观 ...

  6. 【spring源码系列】之【xml解析】

    1. 读源码的方法 java程序员都知道读源码的重要性,尤其是spring的源码,代码设计不仅优雅,而且功能越来越强大,几乎可以与很多开源框架整合,让应用更易于专注业务领域开发.但是能把spring的 ...

  7. spring源码学习之默认标签的解析(一)

    继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...

  8. Spring源码分析(十)注册解析的BeanDefinition

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 对配置文件解析完成后,获取的beanDefiniton已经可以进行使用了 ...

  9. Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器

    aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...

随机推荐

  1. java中args是什么意思?

    1. 字符串变量名(args)属于引用变量,名字代号而已,可以自己取的. 2.总的来说就是个存放字符串数组用的, 去掉就不知道 "args" 声明的变量是什么类型了. 3.如果有 public sta ...

  2. JAVA多线程学习十七 - 面试题

    前面针对多线程相关知识点进行了学习,那么我们来来看看常见的面试题: 1. 空中网面试题1 package com.kongzhongwang.interview; import java.util.c ...

  3. Sublime Text 官方网站 http://www.sublimetext.com

    Sublime Text 官方网站 http://www.sublimetext.com

  4. Foundation框架介绍

    1.Foundation框架介绍 什么是框架? 众多功能\API的集合 框架是由许多类.方法.函数.文档按照一定的逻辑组织起来的集合,以便使研发程序变得更容易,在OS X下的Mac操作系统中大约有80 ...

  5. 用maven在MANIFEST.MF文件中的Class-Path中增加当前目录(.)

    Xml代码 <configuration> <archive> <manifest> <mainClass>com.dongwei.test.Main& ...

  6. 关于unix下cp命令复制权限不够的问题

    例如要将mysql-5.7.17-macos10.12-x86_64.tar.gz 拷贝到/usr/local目录下时,执行cp mysql-5.7.17-macos10.12-x86_64.tar. ...

  7. [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush

    [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush 目录 [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush 0x0 ...

  8. python使用泛型

    所谓的泛型, 就是将数据类型作为参数进行传递, 即在我们用的时候确定数据类型, 这是一种在面向对象语言中经常使用的特性 一般类使用 以SQLAlchemy举例 比如: 我们统一写个将数据保存到数据库的 ...

  9. java Excel 简单工具

    我就简单的分享一下我常用的工具 这次由于个人问题工具注释全部乱码差点无法还原,也是为了防止数据丢失后期找不到再次保留方法把. 调用工具个别方法 <dependency> <group ...

  10. Jenkins项目迁移JOB

    Jenkins项目迁移JOB:从旧的迁移到新的Jenkins上 北京介绍 今天由于迁移Jenkins服务器,需要完全拷贝Jenkins里的Job. 经实验,直接在服务器上移动Jobs目录下的文件即可. ...