Spring解决循环依赖

一、循环依赖

循环依赖指两个或以上的对象之间出现相互引用的情况。Spring中出现循环依赖的情况包括:

  1. 构造器注入的循环依赖。
  2. 属性注入的循环依赖。

 

二、检测循环依赖

在Bean的创建过程中给正在创建的对象Mark上标记,如果循环依赖了则会递归调用,这时发现对象仍反正在创建中,就会抛出异常。

三、Spring解决循环依赖

在spring中解决的是属性注入的方式造成的循环依赖,而构造方法的循环依赖因为在实例化阶段就无法进行所以不能解决。其中prototype类型的对象属性注入循环依赖也无法解决,解决的是默认的单例属性注入造成的循环依赖。

Spring解决单例属性注入循环依赖主要是由三步组成:

  1. createBeanInstance:实例化,调用对象的构造方法实例化对象。
  2. populateBean:填充属性,主要对bean依赖注入进行填充。
  3. initializeBean:初始化bean,完整的bean对象初始化完成。

为了解决循环依赖,Spring使用的方案是三级缓存。 

/** 一级缓存:保存所有初始化完成的单例Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

/** 二级缓存:保存刚从工厂获取实例的单例Bean,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 三级缓存:保存单例对象工厂 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

核心代码:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//从一级缓存中获取对象
    Object singletonObject = this.singletonObjects.get(beanName);
    
    //isSingletonCurrentlyInCreation判断当前bean是否正在创建。
    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 != NULL_OBJECT ? singletonObject : null);
}

 过程分析:

整个getSingleton()方法的过程就是开始从一级缓存获取完成初始化的bean,如果获取不到再到二级缓存获取需要完成依赖注入的bean,如果还是获取不到就到三级缓存中获取bean的工厂,再获取了刚实例化的bean之后,将bean放入二级缓存再将bean的工厂从三级缓存中是删除。

并且在createBeanInstance()方法之后,通过private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);对象将bean的名字加入提前曝光在spring中。此时并没有完成第二依赖注入和完成初始化。

整体分析:

在A的属性包含B,B的属性包含A的循环依赖情况下。首先A进行第一步从三级缓存中获取了实例加入到二级缓存,并且提前暴露在spring中。然后发现注入属性时发现需要B,从而又对B进行第一步实例化对象,B发现注入属性需要A,这时从二级缓存中获取到了A,并且在属性注入后也完成了第二步和第三步,也加入到一级缓存。此时回调到A,在顺利获取到B的实现依赖注入后也完成了第二步和第三步,加入到以及缓存。此时整个过程完成B拿到了A的引用,A也拿到了B的引用。Spring利用三级缓存顺利解决了循环依赖。

 四、@Lazy

@Lazy注解可以应用在各个不同的地方,这里要说的是使用@Lazy注解加在@Autowired注解的上方时的方式。当有时Spring未能帮我们解决循环依赖的问题,启动报错时我们可以这样做:

@Service
public class A {
	
	@Lazy
	@Autowired
	private B b;
}

@Service
public class B {

	@Autowired
	private A a;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值