Spring循环依赖问题--缓存的妙用

本文深入探讨了Spring框架如何处理循环依赖问题,主要通过三级缓存机制实现。一级缓存存储完全创建的单例Bean,二级缓存存放初始化中的Bean,而三级缓存则用于解决AOP代理对象的循环依赖。文章详细解释了每个级别的缓存作用,并通过代码示例展示了循环依赖的创建流程和解决过程。
摘要由CSDN通过智能技术生成

作者并不是只有自己的观点。这篇文章是在作者看了关于spring循环依赖的文章之后总结出来的。

一般来说我们不会出现很多的循环依赖问题,但是在某些业务逻辑中导致了需要使用,还是不可避免的。比如多个service层的业务互相调用等...

针对这个spring循环依赖问题我们今天来探讨一下,如果作者写的有问题希望大家提出,我一定及时改正。

在平常我们的使用中,如果出现了循环依赖很好解决,但是在spring的ioc容器中是一个非常棘手的问题,spring在面对ioc中的循环依赖问题时进行了很多的处理来保证最终的ioc容器正常运行,期中最为关键的就是缓存的使用,那么我们来看看spring是如何巧妙的使用缓存来解决循环依赖问题吧。

何为循环依赖:

 

@Component
public class X {

	@Autowired
	private Y y;

	public X() {
		System.out.println("X is Created");
	}
}
@Component
public class Y {

	@Autowired
	private X x;

	public Y() {
		System.out.println("Y is Created");
	}
}

那么我们具体在创建的流程中是如何的呢?

 我们其实在看这张图之前可以猜测一下,创建对象的过程

spring对象的创建过程:

创建对象-->创建实例-->填充属性-->初始化

那么如果我们不去解决循环依赖,很明显,他会超时,一直A获取B,B获取A,就进入了死循环。

那么spring为了解决就引入了三级缓存,

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

	/** Cache of singleton factories: bean name to ObjectFactory. */
    三级缓存
	private final Map<String, ObjectFactory<?>> 
        singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
    二级缓存
	private final Map<String, Object> 
        earlySingletonObjects = new ConcurrentHashMap<>(16);

 一级缓存:也叫做单例池,也就是存储所有spring-ioc中需要单例实例化的Bean,如果你添加了@Lazy(懒加载)@Scope(prototype)(典型)等一些他是不会被ioc主动实例化到这个池中的,所以spring中的getBean方法就是从这个Map中拿实例的。

AnnotationConfigApplicationContext
         a = new AnnotationConfigApplicationContext(App.class);
		X x = a.getBean(X.class);
		System.out.println(x);

二级缓存:初始化中的Bean,也叫做半成品池,当你出现了循环依赖的时候,比如我们生成A,需要加载B,而B又需要注入A,此时的A是没有经过上面我们写的初始化这一步,那么也就是不是一个完整的对象,我们是肯定不能在单例池里面找到的,那么这个时候我们会将A做个标记,放入二级缓存内,供B注入,然后B对象就创建好了,B就会存在于单例池中,A中的属性填充B,就可以正常填充了,这样A,B就创建好了。这也就解决了spring-ioc容器中的循环依赖问题。

那么到这里我相信读者肯定会有问题:二级换粗不是可以解决循环依赖问题吗?那么三级缓存存在的意义何在呢?

这个时候我们就要知道一件事,AOP(面向切面)中存在的动态代理技术,很多时候我们需要的Bean对象并不是直接拿到的原对象实例,而是需要通过CGlib动态代理之后生成的代理对象,那么我们就会发现,我们需要A的代理对象,属性填充中的B也是代理对象,这个时候,我们就发现二级缓存无法解决这个问题了。

所以spring因此为了解决AOP+循环依赖,则产生了三级缓存来解决,那么三级缓存是什么呢?

三级缓存==>工厂池

因为我们创建A的代理对象的时候,我们需要填充属性B,这个时候spring会先去单例池中找B,肯定是找不到的,然后去半成品池中找代理对象B,很明显,还是找不到,这个时候他就去调用工厂池中的factory(B),创建一个提前引用的B的代理对象,供A使用

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);
			}
		}
	}

//发现是AOP的代理对象出现了循环依赖问题,需要提前引用
@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
		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;
	}

我们平常的动态代理都是在创建完对象,初始化的时候再生成对象的,而AOP这里显然不行。

这个bean工厂不仅可以暴露早期bean还可以暴露代理bean,如果存在aop代理,则依赖的应该是代理对象,而不是原始的bean。

而暴露原始bean是在单例bean初始化的第2步,之后填充属性,生成代理对象,这就矛盾了,A依赖到B并去解决B依赖时,要去初始化B,然后B又再依赖A,而此时A还没有执行代理的过程,所以,需要在填充属性前就生成A的代理并暴露出去,第二步的时候就产生代理对象就刚刚好符合我们的需求。

所以三级缓存才是所有缓存里面最值得学习的一层缓存,完美的解决了AOP代理锁带来的循环依赖问题。

如果作者哪里写的不好或者是有问题欢迎各位读者大佬来指出,我会及时改正,谢谢您的阅读!
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值