转:spring循环依赖

什么是循环依赖
  • 构造器注入
@Component
public class A {
// @Autowired
// private B b;
 public A(B b) {

 }
}


@Component
public class B {

// @Autowired
// private A a;

 public B(A a){

 }
}
  • setter方式注入
@Component
public class A {
    // A中注入了B
 @Autowired
 private B b;
}

@Component
public class B {
    // B中也注入了A
 @Autowired
 private A a;
}
什么情况下循环依赖可以被处理
  • 出现循环依赖的Bean必须要是单例
  • 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)
    在这里插入图片描述

Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A(所以第三种情况能够被spring解决)

分析
Spring在创建Bean的过程中分为三步
  • 实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法(简单理解就是new了一个对象)
  • 属性注入,对应方法:AbstractAutowireCapableBeanFactory的populateBean方法(new出来的对象填充属性)
  • 初始化,对应方法:AbstractAutowireCapableBeanFactory的initializeBean(执行aware接口中的方法,初始化方法,完成AOP代理)
看getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
  • singletonObjects:一级缓存,存储的是所有创建好了的单例Bean
  • earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
  • singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂
getSingleton(beanName, singletonFactory)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {

            // ....
            // 省略异常处理及日志
            // ....

            // 在单例对象创建前先做一个标记
            // 将beanName放入到singletonsCurrentlyInCreation这个集合中
            // 标志着这个单例Bean正在创建
            // 如果同一个单例Bean多次被创建,这里会抛出异常
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // ...
            // 省略catch异常处理
            // ...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

上面的代码我们主要抓住一点,通过createBean方法返回的Bean最终被放到了一级缓存,也就是单例池中

三级缓存真的提高了效率了吗
  • 没有进行AOP的Bean间的循环依赖
    • 这种情况下三级缓存根本没用!所以不会存在什么提高了效率的说法
  • 进行了AOP的Bean间的循环依赖
    • 以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程
    • 上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。
      对于整个A、B的创建过程而言,消耗的时间是一样的

使用了三级缓存:
在这里插入图片描述

不使用三级缓存,直接在二级缓存中:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fFbx3hB1-1596767879880)(AD9E82A6284F465893779560E9BB07AA)]

Spring是如何解决的循环依赖

答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取:

第一步,先获取到三级缓存中的工厂;

第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。

当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?

如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值