透过源码捋清Spring如何解决循环依赖

Spring如何解决循环依赖?

相信不少小伙伴在面试过程中被问到过这个问题,也有不少小伙伴看到这个问题会脱口而出:三级缓存!但是,你真的了解spring解决循环依赖的过程吗?三级缓存是哪三级呢?他们是如何发挥作用的呢?

解决循环依赖的核心类——DefaultSingletonBeanRegistry

核心成员变量

不少同学一定很好奇三级缓存究竟是哪三级呢,他们是以什么样的形式来发挥作用的呢?

// 单例对象的缓存:bean名称到bean实例。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 当前正在创建的bean的名称。
private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// 早期单例对象的缓存:bean名称到bean实例。
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • singletonObjects:用于保存单例Bean对象,其键为Bean名称,值为单例Bean对象。Spring容器启动时会将所有的单例Bean创建并保存在该成员变量中,以供后续的Bean注入和依赖查找使用。——这里面保存的是完整的,经历了创建和初始化之后的完整Bean对象
  • singletonsCurrentlyInCreation:存放当前正在创建的单例bean的名称,在getSingleton方法中,如果singletonObjects、singletonFactories和registeredSingletons中都没有对应的单例对象,就会将当前bean的名称存放在其中,表示当前bean正在创建中。——这里面保存的是正在创建的Bean
  • earlySingletonObjects:存放早期暴露出来的单例对象,即在创建单例对象过程中需要用到当前bean的单例对象,但是当前bean还未创建完成,此时就会将当前bean的代理对象(代理对象可以替代当前bean完成一些操作)存放在earlySingletonObjects中。——这里面保存的是创建完成,还未初始化的Bean

举个例子:有一个BeanA依赖来BeanB,但是BeanB还未完成创建,先对BeanA生成一个临时的代理对象C使BeanA可以用但是不是很完善,并将BeanA存放到earlySingletonObjects容器中,等待BeanB创建完成后再替换回来。

核心方法——getSingleton

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 快速检查没有完整bean
   Object singletonObject = this.singletonObjects.get(beanName);
   // 检查成员变量正在创建的bean中有没有
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
   		// 如果没有完整的bean,并且当前bean正在创建,尝试从半成品中寻找,如果找到了意味着很大概率发生了循环依赖。
   		//比如A和B循环依赖,所以A在创建之后会放在earlySingletonObjects中,初始化B的时候需要A,此时会尝试在earlySingletonObjects寻找A
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
      // 如果半成品里也没有,并且允许早期引用
         synchronized (this.singletonObjects) {
            // 加锁防止其他线程修改
            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) {
                  // 通过工厂方法创建出来这个bean,并将他放到earlySingletonObjects中
                     singletonObject = singletonFactory.getObject();
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     // 因为是单例模式,所以可以把工厂删掉了,以后就用它了
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}

问题:我们只看见了将Bean放入了earlySingletonObjects,什么时候放入到singletonObjects呢?

在SpringBoot的启动过程中实际会有两次移除earlySingletonObjects的操作。

第一次尝试移除:添加Singleton Factory

AbstractAutowireCapableBeanFactory类的doCreateBean方法,代码有点长,我忽略了其他不重要信息。有兴趣的小伙伴可以自己去源码中阅读。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
		/** 忽略部分代码*/
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//添加Singleton Factory会移除一次,虽然此时还没有创建,没错就是这么严谨
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			// 初始化bean,这个方法里的代码在标题《删除earlySingletonObjects放入singletonObjects:完成bean后置处理器》中
			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);
			}
		}
		/**忽略部分代码*/

addSingletonFactory方法代码:

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);
			}
		}
	}
删除earlySingletonObjects放入singletonObjects:完成bean后置处理器
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			//完成创建,这个方法调用链和本文最紧密的部分就是上面最开始的代码“getSingleton”
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
		// 完成bean后置处理器,这个调用链与本文最紧密的代码在下方
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	
protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			// 移出earlySingletonObjects
			this.earlySingletonObjects.remove(beanName);
			// 存入registeredSingletons。此时该bean已经完整了
			this.registeredSingletons.add(beanName);
		}
	}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值