循环依赖的底层原理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了循环依赖的底层原理相关的知识,希望对你有一定的参考价值。
参考技术A从 Spring IOC 容器中获取 bean 实例的流程:从context.getBean()方法开始
三级缓存为什么会移除掉?
在三级缓存中找 找到了就会执行AOp 产出代理对象 然后将代理对象放入到二级缓存。三级缓存一定会能到的对象 但不一定 会执行 aop 二级缓存找不到 才会触发aop(通过lambda表达式,执行函数式接口,执行aop) 产生代理对象放入二级缓存,放入之后需要移除掉对应的三级缓存(保证只执行一次aop)如果三级缓存中对象不需要执行aop操作 ,那么产生的对象仍然要放入二级缓存 ,这是放入的对象是原始对象
为什么单例池:concurentHashmap是,二级缓存是hashmap,三级缓存是hashmap?
三级缓存的AOP过程需要加锁以保证操作的原子性
因为三级缓存的函数接口 内部已经加了锁,保证了操作的原子性 所以没必要使用concurenthashmap
问题:源码中加synchronized锁的意义?
背景:
二级缓存中的aService对象是三级缓存中的lambda表达式生成出来的,
他们是成对的,二级缓存中存在代理对象,则三级缓存中不应该存在lambda表达式;
或者说,三级缓存中存在lambda表达式,则二级缓存中不应当有该代理对象
解答:
其中:
是将原始对象信息存入三级缓存的操作,存入的是lambda表达式:
getEarlyBeanReference(beanName, mbd, bean)执行的结果,getEarlyBeanReference会对是否需要提前AOP进行判断,如果需要进行AOP,则生成代理对象放入二级缓存。
其中:
1、A doCreateBean()初始化,由于还未创建,从一级缓存查不到,此时只是一个半成品(提前暴露的对象),放入三级缓存singletonFactories;
2、A发现自己需要B对象,但是三级缓存中未发现B,创建B的原始对象,将带有B对象信息(beanName,bd,原始对象)的Lambda表达式放入singletonFactories;
3、B发现自己需要A对象,从一级缓存singletonObjects没找到,并知道了A对象正在创建,就确认发生了循环依赖,这时候再去二级缓存earlySingletonObjects中寻找A对象,没找到就继续在三级缓存singletonFactories中寻找A对象(一定能找到),于是执行三级缓存中的lambda表达式得到一个代理对象或者是原始对象A(A中属性未赋值),将A放入二级缓存earlySingletonObjects,同时从三级缓存删除对应beanName的表达式;
同理向三级缓存加入对象时,也会从二级缓存中将相同BeanName的记录删除掉,所以二级缓存与三级缓存的之间的来两步操作具有原子性。
4、将A注入到对象B中;
5、B完成属性填充,执行初始化方法,将自己放入第一级缓存中(此时B是一个完整的对象);
6、A得到对象B,将B注入到A中;
7、A完成属性填充,初始化,并放入到一级缓存中
注意:在对象创建开始的时候,会对对象创建状态利用Set:
singletonsCurrentlyInCreation进行记录:是否是正在创建,可用于判断是否发生了循环依赖。
@Lazy注解的作用:
initializeBean
Spring解决循环依赖问题--B站视频讲解
Spring 循环依赖的“常见”面试问题
Spring 依赖注入原理
参考技术A 有时所在项目忙于业务快速迭代,只知道如何应用框架组件,底层原理却忽略了,框架许多精妙设计没掌握到。每天重复流水线工作,业务层面有所提升,但技术原地不动,每次热情高涨探索底层实现,由于种种原因,坚持没多久就放弃了。为了解开放弃探索小伙伴们心中疑惑,本人花了一些时间研究了底层实现。此次分享学习心得为Spring 依赖注入原理,在实际项目中最常见的依赖注入场景,跨业务注解服务互相调用,配置文件引用第三方服务时信息配置。一 什么是依赖注入
通过类setXxx函数,有参构造函数或注解形式为类的成员属性附上值
二 依赖注入有以下方法
下面这几张注入方式经常碰到,在工作一段时间后,沉下心翻看源码后,多年的疑惑才解开。
1 通过xml配置文件为类实例属性附上值常用有
1.1 bean子标签<property name="name" value="test"/> 标签配上值,property标签的name属性值在类中需要对应public权限setXxx函数如下
XML文件配置
<bean id="dataSource" class="test.Db">
<property name="name" value="QQ"/>
</bean>
JAVA类构建
package test;
public class Db
private String name;
public void setName(String name)
this.name = name;
1.2 bean子标签<constructor-arg name="name" value="test"/> 标签配上值,constructor-arg标签的name属性值,在类中构造函数需要对应参数如下
XML文件配置
<bean id="dataSource" class="test.Db" >
<constructor-arg name="name" value="qq"/>
</bean>
JAVA类构建
public class Db
private String name;
public Db(String name)
this.name = name;
2 类属性加上属性自动注入注解
package test;
public class Db
@Value("$name")
private String name;
package test;
public class Db
@Autowired
private X x;
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
@Service
public class X
三 依赖注入原理
上面的几种形式在实际项目经常碰到,如何实现呢?
1 基于bean子标签property注入原理
1.1 加载bean配置文件,解析bean标签属性值,赋值给BeanDefinition实例并存进集合中,执行
finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory),实例化bean(使用反射技术实例化)。
再调用populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw),内部执行使用反射技术(
ReflectionUtils.makeAccessible(writeMethod);
writeMethod.invoke(getWrappedInstance(), value); writeMethod为Method实例),最后调用函数setXxx为属性赋值。
2 基于bean子标签constructor-arg注入原理
2.1 加载bean配置文件,解析bean标签属性值,赋值给BeanDefinition实例并存进集合中,执行
finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory),当bean的factoryMethodName属性值为空,bean的有参构造函数多个且调用了。执行函数autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs)传入参数。
通过BeanDefinition的成员属性constructorArgument获取构造函数参数,
再执行函数
resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues)传入参数,循环遍历构造函数实例,获取出匹配的构造函数。
通过反射实例化并通过构造函数参数为类属性赋值(即执行ctor.newInstance(argsWithDefaultValues),ctor为Constructor<T>类型)。
3 基于注解注入待续
以上是关于循环依赖的底层原理的主要内容,如果未能解决你的问题,请参考以下文章