欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

spring如何解决循环依赖
(last modified Apr 16, 2023, 12:41 AM )
by
侧边栏壁纸
  • 累计撰写 186 篇文章
  • 累计创建 65 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

spring如何解决循环依赖

橙序员
2023-04-16 / 0 评论 / 2 点赞 / 368 阅读 / 2,690 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

在日常开发中我们可能会遇到以下场景:public class A {@Setterprivate String c;@Setterprivate B b;}@AllArgsConstructorpublic class B {private A a;}上述过程中我们发现,A、B中的属性相互依赖,在创

在日常开发中我们可能会遇到以下场景:

public class A {

	@Setter
	private String c;

	@Setter
	private B b;
}


@AllArgsConstructor
public class B {

	private A a;
}

上述过程中我们发现,A、B中的属性相互依赖,在创建A对象时需要设置属性b,但是属性b的依赖对象B又依赖对象A

image-20230415151440725

在如上的循环依赖中,如果为用户自己创建对象,那么我们的实际创建A、B对应对象步骤如下:

1、实例化a对象

2、实例化b对象

3、为a对象的属性进行赋值

4、对象创建完成

相应代码为:

	public static void main(String[] args) {
		A a = new A();
		B b = new B(a);
		a.setB(b);
		a.setC("123");
		System.out.println(b);
	}

那么如何将具有相互依赖的A、B对象交由容器创建呢?

假如有个需求~~~

针对上述循环依赖的场景,我们需要定义我们的前提:循环依赖必须是两个对象之间互相依赖,如果为如下场景:

A a = new A();
B b = new B(new A());
a.setB(b);
a.setC("123");
System.out.println(b);

则循环关系被打破,所以不存在循环依赖

image-20230415151454715

上述case使用spring配置为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="a" class="org.jdbc.demo.A">
		<property name="b" ref="b"/>
		<property name="c" value="1232"/>
	</bean>
	<bean id="b" class="org.jdbc.demo.B">
		<constructor-arg index="0" ref="a"/>
	</bean>
</beans>

在我们根据上述配置创建bean时,现有的spring流程如下:

  1. 实例化a对象
  2. 对a对象进行属性赋值
    1. 获取属性b对应的bean
    2. 实例化b对象
      1. 发现依赖属性a
      2. 获取属性a对应的bean
      3. 。。。。。

我们发现上述流程会一直循环创建,最终会导致OOM:

image-20230415194427986

如何进行需求实现

要想解决上述过程也很简单,我们只要在每次bean实例化时将其先放入缓存,然后再其他对象创建时发现有对该bean的获取时先从缓存中获取即可:

image-20230415194750702

我们的创建步骤如下:

  1. 实例化a对象,将a对象放入工厂的缓存中中
  2. 对a对象进行属性赋值
    1. 获取属性b对应的bean
    2. 实例化b对象
      1. 发现依赖属性a
      2. 获取属性a对应的bean
      3. 从缓存中获取发现有则返回
    3. 对象b创建完成
  3. 对象a创建完成

spring如何实现

bean创建时-添加到缓存中~

基于我们已有的流程,spring的实现思路与我们在进行需求实现时的流程类似,其具体实现的流程图如下:

image-20230415195115076

可以看到创建bean时主要进行了两处改造:

  1. bean如果为单例bean且允许循环依赖,则添加对应的bean工厂到缓存中
  2. bean初始化后,判断循环依赖中的bean是否仍未创建

对应的代码为:

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		/** 
		 * 省略代码--bean的实例化
		 **/
        
        /** 
		 * 省略代码--后置处理器处理
		 **/
        
		// 判断bean是否为单例bean且允许循环依赖且当前bean在创建中
		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");
			}
			//earlySingletonObjects--remove
			//singletonFactories-----add
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
        
		/** 
		 * 省略代码--bean的属性填充和初始化
		 **/
		// bean的循环依赖检测
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    //获取当前bean的所有依赖bean
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
                        //如果依赖bean仍未创建,则添加到未创建的依赖bean集合中
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
                    //依赖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 " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}
		/** 
		 * 省略代码--bean的注册
		 **/
		return exposedObject;
	}


	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
        /** 
		 * 省略代码--后置处理器的调用处理
		 **/
		return exposedObject;
	}
	
	//将对应的bean工厂添加到缓存中
	//earlySingletonObjects--remove
	//singletonFactories-----add
	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获取时-从缓存中获取~

在bean获取时,首先从缓存中获取bean对象:

  1. 首先看缓存中对象中是否存在
  2. 如果不存在则直接使用bean工厂创建单例放入缓存中且返回

流程图为:

image-20230415221537746

代码为:

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// 从缓存中查找单例bean
        //earlySingletonObjects--add
	 	//singletonFactories-----remove
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
            //返回对应的实例
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// 非单例bean存在循环依赖时,则抛异常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// 省略代码--检测对应bean工厂是否有对应的bean定义

			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				//省略代码--检测bean依赖对象是否实例化

				//创建bean实例
				if (mbd.isSingleton()) {
					//earlySingletonObjects--remove
					//singletonFactories-----remove
   					//singletonObjects-------add
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							//创建过程有异常则销毁bean
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				//省略代码--其他bean作用域下bean的获取
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		//省略代码--bean 类型检查
		}
		return (T) bean;
	}

	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}

	//earlySingletonObjects--add
	//singletonFactories-----remove
	@Nullable
	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;
	}

	//获取单例对象
	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) {
				//省略--非核心代码
				try {
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
                //省略--非核心代码
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}
	
	//单例添加方法
	//earlySingletonObjects--remove
	//singletonFactories-----remove
    //singletonObjects-------add
	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);
		}
	}

小结

在上述代码走读后,我们创建对象a的过程如下:

image-20230416002541228

加亿点点技术~

在上述过程中,我们可以看到spring在创建和获取bean时使用了三层缓存,分别是singletonObjectssingletonFactoriesearlySingletonObjects

一级缓存作用

其中singletonObjects是最好理解的,也与本次循环依赖的需求无关,他的目的就是为了在我们代码保证单例bean只会被创建一次

举个例子:

我们通过获取上述示例中的bean a创建两个对象,分别时对象a和对象b:

		BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/application-context.xml"));
		A a = (A) beanFactory.getBean("a");
		A b = (A) beanFactory.getBean("a");

在创建对象b时,我们debug发现BeanFactory中的singletonObjects不为空,在获取bean时不会再执行创建bean的流程而是直接返回:

image-20230415222235195

所以一级缓存的使用保证了我们bean创建的单例属性,避免了bean的重复创建

三级缓存作用

我们在创建bean实例之后,会将相应的bean工厂添加到缓存中,那么这里为什么不是直接存入的实例化后的bean呢?

我们先看bean工厂缓存时都做了哪些操作:

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		//省略代码--bean的实例化
		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");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
	}

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
        //此工厂是否持有将在关闭时应用于单例 bean 的 InstantiationAwareBeanPostProcessor。
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
                //如果bean的处理器中有SmartInstantiationAwareBeanPostProcessor
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

  	public Object getEarlyBeanReference(Object bean, String beanName) {
   		Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        //判断代理对象缓存中是否包含对应bean,没有则添加
    	if (!this.earlyProxyReferences.contains(cacheKey)) {
      		this.earlyProxyReferences.add(cacheKey);
    	}
    	return this.wrapIfNecessary(bean, beanName, cacheKey);
  	}

	//AOP代理对象处理
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
        
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		//如果有advice,则创建代理。
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
            //返回代理对象
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

我们发现bean工厂在创建时会做如下判断:

  • 如果对应的bean有 AOP,就创建一个代理对象;
  • 如果对应的bean没有 AOP,就返回原对象。

所以我们二级缓存中,有可能存的是我们实例化的bean,也有可能存的是代理的bean。

二级缓存作用

如果只有一级和三级,那么我们通过需要对A进行AOP时会发现每次通过缓存的bean工厂生成的代理对象都是不同的。这样就破坏了bean的单例属性。

所以我们使用二级缓存来把代理生成的代理对象和原生对象都保存起来。这样当有如下依赖case时:

image-20230415233758891

我们便能够保证即使在对象a有AOP的情况下,对象a中的属性b、c获取到的代理实例a也是相同的,即:

image-20230416002643864

2

评论区