spring源码学习(二)
本篇文章,来介绍finishBeanFactoryInitialization(beanFactory);这个方法主要是完成bean的实例化,
invokeBeanFactoryPostProcessors(beanFactory);负责把所有的bean扫描到beanDefinitionMap中;
下面来说是如何初始化的
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
我们直接从这个方法开始说起,前面的调用链简单,就不说了
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { /**
* 通过name获取beanName,这里不使用name直接作为beanName有两个原因:
* 1.name可能是以&开头的,表明调用者想获取FactoryBean本身,而非FactoryBean;在beanFactory中factoryBean的存储也是map格式
* <beanName,bean> 只是说,普通的beanName是没有&这个字符串的,所以,需要将name的首字母移除,这样才能从缓存中拿到factoryBean
* 2.还是别名的问题,需要转换
*/
final String beanName = transformedBeanName(name);
Object bean; /**
* 1.从单例池中获取当前bean
* 2.这里是循环依赖的重要方法之一
*
*/
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
/**
* 这里判断bean是否在创建过程中,是第二次调用的时候 才会判断;如果是第一次执行到这里,set集合是空(这里判断的是原型bean)
*/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
} //添加到alreadyCreated集合当中,表示当前bean已经创建过一次了
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
} try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
} // Create bean instance.如果当前bean是单实例的,就调用createBean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
} return (T) bean;
}
粘贴出来的方法删减了一部分代码,我们只说单实例bean的初始化;
在判断当前bean是单实例的时候,会调用createBean;在getSingleton这里,有一行代码,是把当前bean添加到一个set集合中(这个set集合表示当前bean正在创建过程中),
这个set是用来解决循环依赖问题的,在后面,会单独抽出一篇来介绍循环引用是如何解决的,在这里就先跳过;
在createBean的方法中,会调用createBeanInstance(beanName, mbd, args); 这个方法主要是完成bean的初始化,在方法中会调用org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors;
这个后置处理器的determineCandidateConstructors方法来推断使用哪个构造函数来初始化;
这个方法里面,我目前也正在学习,后续会贴出对这个方法的学习笔记,这里先暂时跳过;(我们就认为createBeanInstance方法完成了bean的创建),
推断出使用哪个构造函数之后,会初始化bean,返回的是一个BeanWrapper对象。这里创建出来的仅仅是bean对象;需要经过后面的属性注入,以及初始化,才会变成我们所说的spring bean对象;
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/**
* 创建bean实例,并将bean实例包裹在BeanWrapper实现类对象中返回,
* createBeanInstance中包含三种创建bean的方式
* 1.通过工厂方法创建bean实例
* 2.通过构造方法自动注入的方式创建bean实例
* 3.通过无参构造方法创建bean实例
*
* 如果bean的配置中配置了lookup-method和replace-method 则会使用增强bean实例
*
* 在这个方法中完成了对bean的创建(仅仅是new出来,也就是说在这个方法里面推断要使用哪个构造函数来创建bean对象)
* 然后完成bean的初始化
*
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
} // Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//mpy 第三次调用后置处理器 缓存注解信息
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
} // Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//mpy 第四次调用后置处理器 获取一个提前暴露的对象 用来解决循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} // Initialize the bean instance.
Object exposedObject = bean;
try {
//在populateBean(beanName, mbd, instanceWrapper);方法中完成第五次第六次调用后置处理器
populateBean(beanName, mbd, instanceWrapper);
//在initialzeBean中完成第七次第八次后置处理器调用
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);
}
}
return exposedObject;
}
上面这个方法中,调用第四次后置处理器这里,返回了一个object对象,这个方法是为了完成循环依赖的,放到后面一起讲,
在populateBean中,会完成属性的注入,比如@AutoWired,@Value这个属性值注入
initializeBean方法,主要是调用bean的初始化方法
bean的初始化有三种方式:
1.@Bean注解中指定 initMethod destroyMethod
2.@PostConstruct @PreDestroy
3.实现DisposableBean和InitializingBean接口
在方法执行完之后,会把创建好的bean对象存放到singletonObjects这个map中,这个map存放的是所有实例化好的对象;如果bean是原型的,在第二次getBean的时候,会从这个map中获取到bean对象
从狭义上来讲,singletonObjects就是我们所说的spring容器
spring源码学习(二)的更多相关文章
- [spring源码学习]二、IOC源码——配置文件读取
一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public cl ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
- Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作
写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...
- Spring源码学习
Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...
- Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签
写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- Spring源码学习笔记9——构造器注入及其循环依赖
Spring源码学习笔记9--构造器注入及其循环依赖 一丶前言 前面我们分析了spring基于字段的和基于set方法注入的原理,但是没有分析第二常用的注入方式(构造器注入)(第一常用字段注入),并且在 ...
- Spring 源码学习笔记10——Spring AOP
Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...
- Spring 源码学习笔记11——Spring事务
Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
随机推荐
- gcc编译命令总结
一步到位编译:gcc hello.c -o hello 预处理 -E (.i) 编译 -S (.s) 汇编-c (.o) 连接-o 预处理 gcc -E hello.c -o hello.i -E:仅 ...
- Spring 梳理 - javaConfig在App和webApp中的应用
package com.dxz.demo.configuration; import org.springframework.context.annotation.Configuration; @Co ...
- javascript中数组常用的方法和属性
前言 在javascript中,数组是一种非常重要的数据类型,我们时常会和它打交道,最近在开发项目中频繁的使用到数组,但是自己对数组的众多方法已经是非常模糊了,为了方便自己以后能够更好的使用数组中的属 ...
- Ubuntu 卸载openJDK
如果你在 Ubuntu 软件中心安装过 OpenJDK,请先使用如下命令将其删除: $ sudo apt-get purge openjdk*
- postman工具使用小结
序言 现在,postman在做接口测试方面,发挥着越来越重大的作用,其支持多种请求方式.并可以模拟各种类型的数据请求类型,在实际开发中使用它可以极大的提高开发的效率. 安装postman 1. 安装 ...
- markdown + 七牛云,让写文更容易
常常写博文的人, 总有这样的烦恼: * 文章格式问题,各种文本编辑器格式不统一,在一处写好的文章复制到其他编辑器中格式错乱 * 图片问题,在不同的平台的图片需要重复上传,如果多平台发布很繁琐 由于这样 ...
- [以太坊源代码分析] I.区块和交易,合约和虚拟机
最近在看以太坊(Ethereum)的源代码, 初初看出点眉目. 区块链是近年热点之一,面向大众读者介绍概念的文章无数,有兴趣的朋友可自行搜索.我会从源代码实现入手,较系统的介绍一下以太坊的系统设计和协 ...
- JavaScript中闭包的使用和各种继承介绍
一.什么是闭包? (1)闭包的概念:a.闭包就是函数嵌套时,让局部变量变成自由变量的环境,是一种让局部变量进化的方式. b.定义在一个函数内部的函数. ...
- 【NOIP2016】蚯蚓
Description 本题中,我们将用符号 ⌊c⌋表示对 cc 向下取整,例如:⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3. 蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀 ...
- Ubuntu安装NASM和简单的使用教程
1. 安装 sudo apt-get install nasm 这样nasm就安装好了,终端输入命令: nasm -version 输出版本信息就说明安装成功 2. 使用 创建"hello. ...