spring循环依赖

着重观察refresh方法

前面七个方法是做一些准备工作,第八个方法是真正的spring创建bean开始。本文只介绍关键步骤

refresh方法流程

1.第一个方法

方法一

一个模板方法 允许子类实现beanfactory后置处理

2.第二个方法

方法二

调用beanfactory处理器

3.第三个方法

方法三

注册bean的后置处理器beanpostprocessor

4.第四个方法

方法四

初始化消息源

5.第五个方法

方法五

初始化应用实际事件发布的委托

6.第六个方法

方法六

一个模板方法 子类实现 可定义特定的刷新方法

7.第七个方法

方法七

查询监听器 并注册

8.第八个方法(重点)

方法八8-1

实例化所有非懒加载单利bean

8.1进入方法

8.1-1

红框之前是准备操作,红款真正开始创建bean 此处流程为创建第一个对象A

8.1-2

此处循环遍历所有的beanDefinition(里面记录了bean的基本信息是否懒加载是否单例等),先判断是否是factoryBean对象,如果不是就走创建bean方法,也就是红框,进入是重载方法

getbean重载方法

再次进入到实际执行方法,先尝试获取bean

尝试获取bean

getsingleton方法实现如下

getsingleton

首先从一级缓存singletonObjects中获取bean对象,如果没有则到二级缓存earlySingletonObjects获取对象,如果还是没有,则到三级缓存singletonFactories中查找,如果三级缓存存在,则清除三级缓存中的ObjectFactory对象,通过三级缓存查询的对象调用getObject方法生成的不完全的bean对象放入二级缓存。概念:一级缓存:执行完bean生命周期完整的bean对象。二级缓存:只执行了一部分beanpostprocessor的不完整的bean对象。三级缓存:bean对应的ObjectFactory是一个工厂对象。

getSingleton之后

此处会判断对象是否存在,如果存在则走getObjectForBeanInstance方法,此处第一次不存在,本方法下面再介绍。

getSingleton不存在

首先判断是否创建过,之后如果查不到beandefinition信息则到父类beanfactory获取

获取beandefinition

实例化bean对象,此处传入参数beanName以及一个lambda表达式

实例化bean对象

进入getSingleton,此处又是getSingleton一个重载方法

getSingletion

通过刚刚的lambda表达式调用getObject获取singletonObject

调用lambda表达式方法
lambda表达式方法

lambda表达式调用了createbean方法,进入creatbean方法

实例化之前处理器
具体实现

这边是判断是否需要生成代理对象而不是原对象,分为实例化之前和初始化之后。先看实例化之前。

实例化之前的操作

判断对象所有的beanpostprocessor,如果有实现了InstantiationAwareBeanPostProcessor接口的则执行生成代理操作,如果有实例化之前则会走初始化之后

初始化之后操作

初始化之后就是遍历执行bean后置处理器

设置属性

这边是设置bean是否做过实例化之前增强,如果这一步bean!=null则creatBean流程结束,如果为null则继续

doCreateBean方法

不需要实例化之前增强则会走正常的创建bean流程,进入

creatbean流程

首先从factoryBeanInstance获取bean获取不到则实例化对象

mergedBeanDefinition处理器

实例化之后 执行合并beandefinition类型的处理器,看一下此处理器的实现

mergedBeanDefinition子类实现

可以看到其中有autowired、定时任务、required等注解处理就在这一步

判断

此处判断是否是单例、是否允许循环依赖、是否是正在创建的对象

暴露自己方法

如果满足上述条件则会进入这个方法,同样的会传入一个beanName以及一个lambda表达式对应的是ObjectFactory对象

暴露自己具体

此处是将完成实例化的对象放入三级缓存,并清理二级缓存,并将beanName放入标记已注册set集合中,注意这里是实例化之后将自己暴露在三级缓存,所以如果是构造器注入则无法解决循环依赖。

实例化之后的操作

首先进入populateBean方法,此方法为填充属性的值

填充属性第一步

进入方法后会先执行实例化处理器的后置处理器操作也就是postProcessAfterInstantiation方法

填充属性第二步

1.判断是否注册instantiation处理器并执行;2.是否需要依赖检查,如果需要检查则会判断对应的类型是否被设置。

准备填充属性

执行准备填充属性

填充属性重要代码

返回一个传入属性的对象

创建属性对象方法

此处可以看到再次调用了getBean方法,进入

doGetBean

再次进入doGetBean方法,开始创建对象B

getSingleton

和创建A一样先从三级缓存中查询是否存在对象,后续流程和创建A一样,此处关注属性赋值

b进行属性赋值

此处也是走的doGetBean

a的getSingleton

此处第三级缓存中存有a的ObjectFactory对象

获取a对象

此处会走之前传入的lambda表达式

传入的lambda表达式
具体实现

此处主要是执行实现了smartInstantiationAwareBeanPostProcessor接口的处理器的方法,其中AbstractAutoProxyCreator类实现了此接口。主要是为了处理aop,这一步之后会讲代理对象放入二级缓存,并且删除三级缓存中的对象。

处理器调用的方法

此处将a放入了早期代理引用(早期代理引用指的是循环依赖中只执行了aop处理的不完整的bean)的set集合中,为了之后执行后置处理器不重复执行proxy。

此处为初始化后置处理器

补充:此方法是bean初始化后置处理器执行的aop增强的方法,这里会先到早期引用代理判断对象是否存在如果存在则不需要再做aop处理。

具体方法

此处为AbstractAutoProxyCreator实现接口之后的具体实现方法,主要就是获取所有的增强,并添加到标记是否需要增强的map中 设置为false(不需要) 

真正执行proxy方法

此处执行动态代理,cglib或者jdk并返回。然后属性赋值,完成b的创建(完整的bean),创建完成后会将二级三级缓存中的b清理,并放入一级缓存中。

初始化对象

回到A对象创建,分析一下属性赋值之后初始化的工作内容,进入方法

初始化执行步骤

可以清楚地看到初始化流程会先执行beanPostBeforeInitialiaztion然后执行invokeInitMethods最后执行beanPostAfterInitialiaztion

invokeInitMethods

其中invokeInitMethods方法中会先执行afterPropertiesSet然后才是initmethod。(@PostConstruct注解方法优于这两个方法) 

初始化之后

如果是标记循环依赖的对象则到二级或三级缓存获取对象,并返回,此时完成docreatbean方法。最后将完整的a的bean对象放入一级缓存并清空二级三级缓存。

2021-1-28----补充

补充一下@autowired装配。之前说到了mergedBeanDefinition类,这边会将需要autowired等注解进行记录

AbstractAutowireCapableBeanFactory.populateBean方法

在执行applyPropertyValues方法之前会先判断是否有实例化处理器以及是否需要自动注入,如果需要就会走如图判断,遍历所有处理器,并执行符合要求的

AutowiredAnnotationBeanPostProcessor类
AutowiredAnnotationBeanPostProcessor父类关系图

可以看出autowired注解处理器是InstantiationAwareBeanPostProcessor的子类,会走其中的逻辑。

AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues

首先获取到所有autowired的元数据,然后进行注入。

InjectionMetadata.inject

找出全部的element并逐个注入(如何解析出element的接下来讲)

AutowiredAnnotationBeanPostProcessor.inject

这边先查缓存,然后获取被依赖对象以及依赖对象以及类型,然后通过beanFactory的resolveDependency方法获取值

DefaultListableBeanFactory.resolveDependency

首先判断是不是必须的,是不是objectfactory,然后这边主要看最后一个else的内容,获取懒加载对象,如果没有则走doResolveDependency方法。

DefaultListableBeanFactory.doResolveDependency方法

这个方法中其他的也是一些判断和获取信息,主要看这行代码,就是加载所需要的类

DependencyDescriptor.resolveCandidate方法

到这里就可以看到熟悉的beanfactory.getBean方法了,然后就是走创建bean的流程了

如果我没猜错,等我在看几次,后续这个文章应该会有补充以及修改!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容