一、循环依赖问题
问题描述:A依赖B,B依赖A,形成闭环
问题产生场景一:构造器
@Service
public class A{
private B b;
public A(B b){
this.b = b;
}
}
@Service
public class B{
private A a;
public B(A a){
this.a = a;
}
}
项目启动失败:产生循环依赖
原因:
1、Spring根据构造器去实例化A
2、Spring去实例化A构造器所需的参数B
3、Spring根据构造器去实例化B
4、Spring去实例化B构造器所需的参数A
5、最后上面陷入死循环,导致项目启动失败。因为A未完成实例化,所以没把获取A的方法放入三级缓存,程序因此会陷入不断去实例化A和B的死循环。
问题产生场景二:属性注入
@Service
public class A{
@Resource
private B b;
}
@Service
public class B{
@Resource
private A a;
}
项目启动成功:spring解决循环依赖问题
二、Spring如何解决循环依赖问题
根本原因
其根本原因在于DefaultSingletonBeanRegistry类中的三级缓存
/** 一级缓存: 单例bean名称->单例bean对象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二级缓存: 单例bean名称->单例bean对象,未初始化完成bean */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** 三级缓存: 单例bean名称->单例bean的创建方法 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存中的ObjectFactory是一个函数式接口,它最后指向的方法是getEarlyBeanReference(beanName, mbd, bean)
/**
* 获取对指定 bean 的早期访问的引用,通常用于解析循环引用。
* @param beanName bean 的名称(用于错误处理目的)
* @param mbd bean 的合并 bean 定义
* @param bean 原先的bean实例
* @return 要公开为 bean 引用的对象
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
// 判断bean是否是我们自定义的,返回此工厂是否拥有将在创建时应用于单例 bean 的 InstantiationAwareBeanPostProcessor
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
源码解析
项目代码如下:
@Service
public class A {
@Resource
private B b;
}
@Service
public class B {
@Resource
private A a;
}
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
接下来开始在run方法上debug,这里直接跳到关键部分,同时代码太多直截取关键部分
第一部分:创建Bean
第一步:AbstractApplicationContext类的refresh()方法
// Instantiate all remaining (non-lazy-init) singletons.
// 这里实例化所有非懒加载的单例对象
finishBeanFactoryInitialization(beanFactory);
第二步:DefaultListableBeanFactory类的preInstantiateSingletons方法
// 这里会获取很多bean名称,包含我们需要实例化的bean名称,a和b
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 这里开始触发非懒加载的bean初始化
for (String beanName : beanNames) {
...
// 最后我们的bean会进入这里
getBean(beanName);
}
第三步:AbstractBeanFactory的doGetBean方法
// 这里从三级缓存中获取当前bean,第一次A进来返回为null
Object sharedInstance = getSingleton(beanName);
......
// 这里创建A实例
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
...
});
/**
* 返回在给定名称下注册的(原始)单例对象。
* 检查已经实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)。
* 这里就是从三级缓存中拿对象
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 快速检查没有完整单例锁的现有实例
// 从一级缓存从获取
Object singletonObject = this.singletonObjects.get(beanName);
// 第一次A对象初始化进来这里时,直接结束,由于判断返回false,当前对象不在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
第四步:DefaultSingletonBeanRegistry的getSingleton方法,获取bean实例
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
...
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
......
// 这里会将当前bean名称放入,当前正在创建bean的Set集合中,与第三步isSingletonCurrentlyInCreation方法关联
beforeSingletonCreation(beanName);
boolean newSingleton = false;
......
// 这里去获得真正的bean实例,从这里进入第五步
singletonObject = singletonFactory.getObject();
newSingleton = true;
......
// 这里会将当前bean名称从当前正在创建bean的Set集合中删除
afterSingletonCreation(beanName);
......
// 将bean放入一级缓存,并将其从二级三级缓存中删除
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
第五步:AbstractAutowireCapableBeanFactory的createBean方法
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
......
// 去创建bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
......
}
第六步:AbstractAutowireCapableBeanFactory的doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {
...
// 这里开始去创建bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
.......
// 这里将bean放入三级缓存singletonFactories中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
...
// 填充属性,这里将把B填充进去
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
.......
return exposedObject;
}
第七步:AbstractAutowireCapableBeanFactory的createBeanInstance这里创建了bean
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
......
// 这里由于我们未定义任何构造器,直接进入最底下默认的初始化bean方法
return instantiateBean(beanName, mbd);
}
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
......
// 这里创建了bean,我们看看里面有什么
return BeanUtils.instantiateClass(constructorToUse);
}
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
.......
// 这里最终用反射创建了bean
return ctor.newInstance(argsWithDefaultValues);
}
第二部分:初始化Bean
第八步:现在回到第六步中的addSingletonFactory方法,将bean放入三级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 当一级缓存不存在该bean的情况下
if (!this.singletonObjects.containsKey(beanName)) {
// 将创建bean的方法放入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 从二级缓存中删除当前bean
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
第九步:第六步中的populateBean方法,填充A的属性,我们直接进入到关键代码InjectionMetadata的inject方法
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
// 这里把A的属性b填充进去,target就是A
field.set(target, getResourceToInject(target, requestingBeanName));
}
......
}
第十步:这时候顺着第九步的getResourceToInject一直下去,你会发现又回到了第三步的doGetBean方法,不过这次get的是B,接下来就是把上面的九步再走了一遍,不过这次是B而已,这里就不再重复了。
最后,B同样也需要注入a,你会发现代码走下去,B也到了populateBean,这时候就是继续走下去,你会发现最后填充B中的属性的时候,去取A也走到了doGetBean方法,还记得第三步的getSingleton方法吗
/**
* 返回在给定名称下注册的(原始)单例对象。
* 检查已经实例化的单例,并允许对当前创建的单例进行早期引用(解决循环引用)。
* 这里就是从三级缓存中拿对象
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存从获取
Object singletonObject = this.singletonObjects.get(beanName);
// 第一次A对象初始化进来这里时,直接结束,现在B来取A进入这里,由于前面A在第四步执行过beforeSingletonCreation方法,此时判断通过
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 从一级缓存从获取
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 从三级缓存中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 删除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
三级缓存中的ObjectFactory是一个函数式接口,它最后指向的方法是getEarlyBeanReference(beanName, mbd, bean)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
这里他从三级缓存中获取bean,这时候循环依赖问题得到解决,直接从三级缓存中获取bean,然后放入二级缓存,删除三级缓存,返回bean。
第十一步:接下来B回到第四步,先是afterSingletonCreation将B从正在创建的bean的Set集合中删除,然后执行addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 将B放入一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 从三级缓存删除B
this.singletonFactories.remove(beanName);
// 从二级缓存删除B
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
第十二步:B创建结束并返回给A,然后A再重复第十一步,Bean实例化完成,循环依赖解决
三、思考
上述DEBUG过程中发现,其实二级缓存也可以解决循环依赖问题,直接将二级缓存和三级缓存合并,把bean放入三级缓存的代码改成放入二级缓存。
为什么要使用三级缓存
其实和AOP代理有关,我们改造下代码,在进行DEBUG。增加一个切面
@Aspect
@Component
public class DemoAspect {
@Before(value = "execution(* com.example.demo.cycle.*.*(..))")
public void before(){
System.out.println("aop----before");
}
}
其他地方都没变,在填充B的属性从三级缓存中拿A时,这时候返回将不在是简单的一个bean,而是一个代理类
// 注意这里开始,bean相当于分成两个,一个进入三级缓存,一个继续进行初始化
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Object exposedObject = bean;
// 这里填充bean中的属性,从最开始A到这里->B到这里->B填充属性A,从三级缓存中获取A,这时给的是A的代理类,但最开始的A在这里还是一个普通的bean
populateBean(beanName, mbd, instanceWrapper);
// 这里从二级缓存中取出A的代理类
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 最后返回的是A的代理类
exposedObject = earlySingletonReference;
}
但是这里仔细查看你会发现,就算是为了代理类,还是能使用二级缓存实现,只要在一开始就将代理类放入二级缓存,也能实现,改造如下:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 直接将bean从三级缓存中拿出放入二级缓存,再从三级缓存中删除,相当于未使用三级缓存
getSingleton(beanName, true)
为方便快速验证我们的想法,使用IDEA的评估表达式(快捷键Alt+F8)直接在DEBUG中执行getSingleton方法来验证。
执行过后,项目同样正常启动无异常,所以二级缓存也可以解决循环依赖问题,那么为什么Spring还要使用三级缓存呢?
根本原因
这时候需要对比两种不同情况下,bean的完整创建过程。
改动源码前
存在AOP,无循环依赖问题:
1、构造器反射创建bean
2、populateBean方法填充属性
3、initializeBean方法初始化bean,在方法的最后(也就是bean生命周期的最后一步)获取应用的BeanPostProcessors列表,遍历执行其postProcessAfterInitialization方法,通过列表中的AnnotationAwareAspectJAutoProxyCreator创建bean的代理类
存在AOP,有循环依赖问题:
1、构造器反射创建bean
2、populateBean方法填充属性,使用到三级缓存创建bean代理类
3、initializeBean方法初始化bean
改动源码后
再看看我们改造后的代码过程有什么不同
存在AOP,有无循环依赖问题:
1、构造器反射创建bean
2、创建bean代理类放入二级缓存
2、populateBean方法填充属性
3、initializeBean方法初始化bean
总结
对比上述过程,你会发现改造后,不管存不存在循环依赖问题,都在bean填充属性和初始化之前创建了代理类,这不符合Spring的设计原则。而原本的代码,只有在发送循环依赖问题的情况下,没办法,才会在填充属性是创建代理类。Spring的设计之初就是在生命周期的最后一步完成代理