Spring循环依赖-深度分析

文章详细探讨了Spring框架中循环依赖的解决策略,包括一级、二级和三级缓存的作用,特别是在处理循环引用和AOP代理时的机制。Spring使用三级缓存是为了在保证线程安全的同时解决循环依赖问题,尤其是在存在代理对象的情况下的正确处理。文章还指出了在某些特定情况下,如构造函数依赖注入和使用@Lazy、ObjectFactory时,Spring的循环依赖解决方案可能失效或导致问题。
摘要由CSDN通过智能技术生成

Spring 循环依赖终极版

Spring循环依赖网上解说千遍一律,各有各的见解,但总归大体还是一致的。
我们带着问题的角度去看,循环依赖。

  • 1.什么是循环依赖
  • 2.循环依赖的解决方案有哪些
  • 3.Spring 是怎么解决循环依赖的。
    Spring 为什么使用三级缓存,一级,二级可以吗?
    此问题(网上解释众说纷纭)这也是我们今天要聊的主要问题。
  • 4.Spring 完全解决了循环依赖问题吗? 有哪有些情况下解决不了?是否可以其他方式解决?

让我们进入主题:
首先看几张图什么是循环依赖:

在这里插入图片描述
在这里插入图片描述

正常情况下单例实例化:

@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
					//在完整的单例锁中一致地创建早期引用(只调用一次singletonFactory.getObject()创建一次代理如果存在,否则直接返回实例)
					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);
								//删除三级缓存中的缓存的ObjectFactory引用
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
        //.........................省略以上代码
				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
						    //创建
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		//.....................省略以下代码
		return (T) bean;
	}
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		//.....................省略以上代码
		try {
		    //执行创建bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// 急切地缓存单例,以便能够解析循环引用,即使是在由BeanFactoryAware等生命周期接口触发时.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//添加到singletonFactories(常说的三级缓存)
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
		    //用bean定义中的属性值填充给定BeanWrapper中的bean实例
			populateBean(beanName, mbd, instanceWrapper);
			//初始化给定的bean实例,应用工厂回调(BenFactoryAware)以及init方法和bean后处理器。对于传统定义的bean,
			//从createBean调用;对于现有的bean实例,从initializeBean调用。
			/*----------------------------------------------------------
			 *BeanNameAware->BeanClassLoaderAware->BeanFactoryAware
			 *postProcessBeforeInitialization
			 *InitializingBean.afterPropertiesSet()--->invokeCustomInitMethod
			 *postProcessAfterInitialization
			 *
			 -----------------------------------------------------------*/
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}
         //一处关键点;

		if (earlySingletonExposure) {
		//获取早期bean引用,earlySingletonObjects(常说的二级缓存)
			Object earlySingletonReference = getSingleton(beanName, false);
			//如果不为空
			if (earlySingletonReference != null) {
			 // exposedObject 是被initializeBean方法执行后返回的(可能被包裹,
			 //比如:@Async异步(postProcessAfterInitialization)spring设计初衷对于普通bean(不存在循环引用)
			 //就是在此处对实例bean进行包裹(代理))
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				//allowRawInjectionDespiteWrapping :属性默认为false;
				//意思:是否在循环引用的情况下注入一个原始bean实例,即使注入的bean最终被包装
				//就是注入的bean 与最终生成的bean 不一样。
				//hasDependentBean:确定是否为给定名称注册了依赖bean。
				//意思:就是当前beanName所生成的bean是否有被其他实例所依赖;
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
				    //获取依赖当前beanName实例的beanName集合
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
					//判断给定bean名称(dependentBean)是否已经至少创建过一次的bean的名称
					//没有创建添加到actualDependentBeans实际依赖bean
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					//如果不为空,即存在注入的bean 与最终生成的bean不一致;
					//抛出循环依赖异常
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}
/**
*如果需要,添加给定的单例工厂来构建指定的单例。
*在单例对象急切注册时调用,例如能够解析循环引用。
*/
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);
			}
		}
	}
/**
*获取一个引用,以便尽早访问指定的bean,通常用于解析循环引用
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				   /*
                    * 获取一个引用,以便尽早访问指定的bean,通常用于解析循环引用。
                    * 这个回调使后处理程序有机会在早期公开包装器(代理)——也就是说,在目标bean实例完全初始化之前。
                    * 暴露的对象应该等价于postProcessBeforeInitialization / postProcessAfterInitialization将暴露的对象。
                    * 注意,此方法返回的对象将用作bean引用,除非后处理程序从所述后处理回调中返回不同的包装器。
                    * 换句话说:那些后处理回调可能最终公开相同的引用,也可能从那些后续回调中返回原始bean实例
                    * (如果受影响bean的包装器已经为该方法的调用构建,那么默认情况下它将被公开为最终bean引用)。
                    * 默认实现原样返回给定的bean。
                    */
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
  • 我们来看作用域单例模式下创建过程:

1.只使用一级缓存singletonObject的话
1.在获取getBean时就得对这个一级缓存加锁,防止并发,加锁影响性能,比如懒加载得bean 持有得是代理对象,每次要getBean.
2.尚未初始化得bean(不完整得bean),直接放在一级缓存singletonObject,违背设计,职责等。
2.只使用一级缓存singletonObject和三级缓存singletonFactories
1.三级缓存存得是ObjectFactory得引用,在循环引用时;
多个Bean依赖同一个实例

  • 实例A 依赖实例B
  • 实例B 依赖实例C
  • 实例C 依赖实例A
  • 实例B 依赖实例A

提示:实例B 和实例C 都依赖实例A
如果实例A不存在AOP增强得情况下调用执行三级缓存里面得ObjectFactory.getObject直接返回实例对象则无问题,如存在AOP需要创建代理类,如果没有把ObjectFactory.getObject生成得对象缓存起来,实例C调用生成一次,实例B调用又生成一次。

所以只使用一三级缓存是存在问题得。
现在同事又提了,那使用一级和二级缓存?
3.只使用一级缓存singletonObjects和二级缓存earlySingletonObjects
有同事会说我直接使用二级缓存,直接缓存代理代理对象?
1.spring 最初设计使用三级缓存只针对代理循环依赖,只有存在循环依赖得时候才会去调用三级缓存里得ObjectFactory.getObject生成得对象缓存起来,而我们大多数情况下是不存在循坏依赖得而且spring 也希望设计避免存在循环依赖得情况。Spring boot2.5.x 默认关闭了循环依赖。
2.spring 设计初衷AOP是在beanPostProcessor得后置处理后置处理方法postProcessAfterInitialization增强
提示:可以看一下此篇文章: https://editor.csdn.net/md?not_checkout=1&articleId=128314610
在不存在循环引用得情况下,如果我们也及早得实例化放到二级缓存中,如果我们得实例有其他代理,比如异步等等,这个时候二级缓存得代理与最终要代理得bean 不一致
在二级缓存生成代理后后续初始化流程还使用原始bean(Spring 默认后续流程)

  • 会存在这个时候二级缓存得代理与最终要代理得bean 不一致等等

在二级缓存生成代理后后续初始化流程使用代理得bean(改造spring代码)

  • 后续操作都得一代理bean为基础,违背设计等等。

Spring 在那些情况下循环依赖失效存在问题:

  • 构造函数依赖注入:直接依赖注入抛出异常
    在不改变代码得情况下使用

    • 使用@Lazy
    • ObjectProvider
    • ObjectFactory
  • 属性注入:如果在bean得后置处理器处置处理方法返回得bean对象与给定得bean对象不是同一个对象时,如果二级缓存中存在早期缓存对象
    依赖注入得bean与最终缓存得单例bean不一致。比如:注入得类存在循环依赖和@Async得情况下,抛出异常。
    在不改变代码得情况下使用

    • @Lazy
    • ObjectProvider
    • ObjectFactory
    • Optional

    构造函数依赖

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    属性依赖
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值