Spring中怎么解决循环依赖

发布时间:2021-06-18 18:04:00 作者:Leah
来源:亿速云 阅读:455

今天就跟大家聊聊有关Spring中怎么解决循环依赖,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。


一、循环依赖是什么?

循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。如下图所示:

Spring中怎么解决循环依赖

Spring中的循环依赖,其实就是一个死循环的过程,在初始化 A 的时候发现依赖了 B,这时就会去初始化 B,然后又发现 B 依赖 C,跑去初始化 C,初始化 C 的时候发现依赖了 A,则又会去初始化 A,依次循环永不退出,除非有终结条件。

一般来说,Spring 循环依赖的情况有两种:

构造器的循环依赖。 field 属性的循环依赖。 对于构造器的循环依赖,Spring 是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖,所以下面我们分析的都是基于 field 属性的循环依赖。

在前文 Spring Ioc源码分析 之 Bean的加载(三):各个 scope 的 Bean 创建 中提到,Spring 只解决 scope 为 singleton 的循环依赖。对于scope 为 prototype 的 bean ,Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。

为什么 Spring 不处理 prototype bean 呢?其实如果理解 Spring 是如何解决 singleton bean 的循环依赖就明白了。这里先留个疑问,我们先来看下 Spring 是如何解决 singleton bean 的循环依赖的。

二、解决singleton循环依赖

在AbstractBeanFactory 的 doGetBean()方法中,我们根据BeanName去获取Singleton Bean的时候,会先从缓存获取。
代码如下:

//DefaultSingletonBeanRegistry.java

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存缓存 singletonObjects 中加载 bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // 缓存中的 bean 为空,且当前 bean 正在创建
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 加锁
        synchronized (this.singletonObjects) {
            // 从 二级缓存 earlySingletonObjects 中获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            // earlySingletonObjects 中没有,且允许提前创建
            if (singletonObject == null && allowEarlyReference) {
                // 从 三级缓存 singletonFactories 中获取对应的 ObjectFactory
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //从单例工厂中获取bean
                    singletonObject = singletonFactory.getObject();
                    // 添加到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 从三级缓存中删除
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

这段代码涉及的3个关键的变量,分别是3个级别的缓存,定义如下:

	/** Cache of singleton objects: bean name --> bean instance */
	//单例bean的缓存 一级缓存
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name --> ObjectFactory */
	//单例对象工厂缓存 三级缓存
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name --> bean instance */
	//预加载单例bean缓存 二级缓存
	//存放的 bean 不一定是完整的
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

getSingleton()的逻辑比较清晰:

2.1、三级缓存

看到这里可能会有些疑问,这3个缓存怎么就解决了singleton循环依赖了呢?
先别着急,我们现在分析了获取缓存的代码,再来看下存储缓存的代码。 在 AbstractAutowireCapableBeanFactorydoCreateBean() 方法中,有这么一段代码:

// AbstractAutowireCapableBeanFactory.java

boolean earlySingletonExposure = (mbd.isSingleton() // 单例模式
        && this.allowCircularReferences // 允许循环依赖
        && isSingletonCurrentlyInCreation(beanName)); // 当前单例 bean 是否正在被创建
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    // 为了后期避免循环依赖,提前将创建的 bean 实例加入到三级缓存 singletonFactories 中
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

这段代码就是put三级缓存singletonFactories的地方,其核心逻辑是,当满足以下3个条件时,把bean加入三级缓存中:

addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) 方法,代码如下:

// DefaultSingletonBeanRegistry.java

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

从这段代码我们可以看出,singletonFactories 这个三级缓存才是解决 Spring Bean 循环依赖的关键。同时这段代码发生在 createBeanInstance(...) 方法之后,也就是说这个 bean 其实已经被创建出来了,但是它还没有完善(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(已经有内存地址了,可以根据对象引用定位到堆中对象),能够被认出来了。

2.2、一级缓存

到这里我们发现三级缓存 singletonFactories 和 二级缓存 earlySingletonObjects 中的值都有出处了,那一级缓存在哪里设置的呢?在类 DefaultSingletonBeanRegistry 中,可以发现这个 addSingleton(String beanName, Object singletonObject) 方法,代码如下:

// DefaultSingletonBeanRegistry.java

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
	        //添加至一级缓存,同时从二级、三级缓存中删除。
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

该方法是在 #doGetBean(...) 方法中,处理不同 scope 时,如果是 singleton调用的,如下图所示:

Spring中怎么解决循环依赖 也就是说,一级缓存里面是完整的Bean。

小结:

总结

现在我们再来回顾下Spring解决单例循环依赖的方案:

举个栗子
比如我们团队里要报名参加活动,你不用上来就把你的生日、性别、家庭信息什么的全部填完,你只要先报个名字,统计下人数就行,之后再慢慢完善你的个人信息。

核心思想:提前暴露,先用着

最后来描述下就上面那个循环依赖 Spring 解决的过程:

http://cmsblogs.com/?p=todo (小明哥)

看完上述内容,你们对Spring中怎么解决循环依赖有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

推荐阅读:
  1. Spring怎么解决循环依赖的问题
  2. spring如何解决循环依赖

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring

上一篇:Mysql中怎么优化慢查询

下一篇:python清洗文件中数据的方法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》