Spring三级缓存源码分析

以两个有循环依赖的Bean(且均被AOP切入,需要被代理):Teacher类和Student类为例

用beanFactory的preInstantiateSingletons方法初始化单例类型的bean:
这里beanFactory是DefaultListableBeanFactory类:

循环beanName拿到对应的beanDefinition进而筛选判断得到非抽象类且是单例类且非懒加载的beanName,然后进行实例化注入到单例池singletonObjects中:
当前先找到Teacher的Bean,进行Teacher的单例实例化:
this.getBean方法进行单例bean实例化:

doGetBean方法:只看重要的
首先尝试从三个缓存中获取该Bean

目前正是IOC容器初始装载阶段,一二三级缓存都还没存各个Bean的引用,当然获取不到,往后看doGetBean方法的代码逻辑:

当前没有父Factory,当前BeanFactory也包含目标Bean的BeanDefinition,所以这段逻辑直接跳过,继续往下看:

getSingleton方法:

调用传进来的lambda表达式:

进入到lambda表达式逻辑中的createBean方法中:

doCreateBean方法:

当前该bean是原生bean,未加代理

然后现在一二级缓存中没有Bean的引用,三级缓存中存在该Bean的beanName与该lambda表达式的K-V对映射关系
populateBean方法是对Bean进行依赖注入,initializeBean是对Bean进行一些额外操作,其中最重要的就是AOP代理类的生成

接着来看:Teacher类中存在Student类,Student类中存在Teacher类,形成循环依赖
在Teacher的Bean的populate方法中对Student进行注入时,仍然会走getBean获取Teacher的Bean的那个过程,在该过程中,Student的Bean也会将自己先添加到三级缓存中,然后也走到populate属性注入方法,查到要进行Teacher的依赖注入,这时候仍走getBean那一套逻辑(上面这些分析图),不过会在这个位置:

从三级缓存中拿到与Teacher的BeanName(key)对应的那个lambda表达式(value)

singletonFactory.getObject()执行lambda表达式的方法:即这里的getEarlyBeanReference方法

进入到getEarlyBeanReference方法:

SmartInstantiationAwareBeanPostProcessor的实现类就有Spring中进行AOP代理操作的代理类:
在getEarlyBeanReference方法中的逻辑是:如果该Bean需要代理就返回该Bean的代理对象,如果不需要代理就将Bean原封不动返回:
所以Student在进行对Teacher的Bean的依赖注入时,从三级缓存获取了lambda表达式并执行其getEarlyBeanReference方法,获取到了Teacher的代理对象,此时Teacher已经进行了代理,并从三级缓存中移除然后放到二级缓存中

此时的状态:Teacher的bean已经被代理并生成了代理对象,从三级缓存中移除放入到了二级缓存中,Student的Bean仍然在三级缓存中

将Teacher的bean的代理对象引用注入到Student的Teacher属性中后,继续进行Student的后续逻辑,
populate方法(属性依赖注入)方法执行完后,开始执行Student的initialize方法,进行AOP代理:

在Student的Bean执行完initialize方法后,得到Student Bean的代理对象
之后的逻辑:

可以看到为getSingleton方法传入的参数allowEarlyReference为false,该参数控制是否允许去三级缓存获取当前Bean,false代表不允许,而此时Student的Bean引用只在三级缓存中存在,则得到的earlySingletonReference为null,后续逻辑直接跳过,直接返回Student的代理对象exposedObject

返回给createBean方法:

再返回给getSingleton方法:

但在getSingleton方法中会将获取到的Bean添加到一级缓存中(Bean的完全体):

至此,Student的Bean的全部创建过程已完结
目前三个缓存中的状态为:Teacher的代理对象引用存在于二级缓存中,Student的代理对象存在于一级缓存中。
Student创建完成后,跳回到Teacher Bean的populate属性依赖注入方法中,将Student的代理对象注入到Teacher的Student属性中,之后执行Teacher Bean的initialize方法进行AOP代理,但目前Teacher的Bean已经被代理过了(是在为Student的Bean进行Teacher Bean的依赖注入时从三级缓存中拿到Teacher的BeanName对应的lambda表达式并执行方法getEarlyBeanReference时被代理的),所以这里产生了一个逻辑矛盾需要解决,Spring是这样解决的:
如果Bean是在被从三级缓存中拿到并进行代理的,该代理过程会把bean以cacheKey-bean的形式存入到earlyProxyReferences这个记录了早期被代理的Bean的Map中:

然后在正常步骤的执行代理阶段即initialize中调用BeanPostProcessor实例类的AfterInitialization方法中进行代理,会先判断earlyProxyReferences中是否已经存在该bean(即该bean已经被代理过了),如果存在就不再进行代理操作,如果不存在则再进行代理操作:

显然在Teacher Bean的intialize方法中,不再需要代理了,那么继续执行后续的逻辑:

后续返回exposedObject然后在一步步返回过程中将其添加到一级缓存中并清除二三级缓存

至此存在循环依赖的Teacher和Student的Bean创建完成。一二级缓存为空,三级缓存中存在Student和Teacher的Bean的代理对象的引用。

总结一下全过程:
首先,一二三级缓存全空
开始:
1.Teacher类的Bean反射调用构造器得到Teacher实例
2.将该实例放入三级缓存
3.进行populate属性注入,遇到Student依赖注入
4.开始去拿Student的Bean,三个缓存都没拿到
5.Student类的Bean反射调用构造器得到Student实例
6.将该实例放入三级缓存
7.进行populate属性注入,遇到Teacher依赖注入
8.从三级缓存中拿到Teacher对应的工厂(lambda表达式)调用getEarlyBeanReference方法
9.在该方法中对Teacher进行代理,并登记到earlyProxyReference中
10.从三级缓存中删除Teacher,将其代理对象引用添加到二级缓存中
11.Student的属性注入(populate)结束,执行initialize进行AOP代理操作得到Student的代理对象
12.将Student代理对象注入到一级缓存中,并返回给Teacher的populate方法中进行Student依赖注入那一步,至此Student创建完成
13.Teacher完成populate属性注入,调用initialize,但earlyProxyReference已经存在Teacher代理对象,就不再在initialize逻辑中进行代理操作
14.从二级缓存中得到Teacher的代理对象并返回
15.将该代理对象添加到一级缓存中,删除其二级缓存
16.Teacher的Bean创建完成
结束

关于循环依赖的一些说明:
如果通过构造器进行依赖注入(即在用构造方法创建Bean时通过构造方法进行属性注入)则无法解决循环依赖,因为Bean的创建过程是:
1.反射调用构造器得到实例对象
2.添加实例对象到三级缓存中,目的是供其他依赖当前Bean的Bean去缓存中能找到该Bean
3.进行populate依赖注入
4.initialize进行代理
如果通过构造器进行依赖注入,那么代表在第一步反射调用构造器的时候就要去获得另一个Bean对象,可问题是当前Bean的引用还没有被装入缓存(第二步),那么在获得另一个Bean时,另一个Bean在缓存池中找不到当前Bean的引用,也就无法完成创建的全过程,就此卡住,解决不了循环依赖

循环依赖只针对两个singleTon单例类型的Bean生效
两个prototype类型的Bean不存在循环依赖,因为依赖注入每次都是创建一个新的
若是一个prototype一个singleton也可以解决循环依赖,只不过区别是prototype只会添加到二级缓存中,不添加到一级缓存中

一级缓存中存储的都是singleton单例的bean,也就是单例池singletonObjects
 

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值