前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring的循环依赖和三级缓存

Spring的循环依赖和三级缓存

作者头像
CBeann
发布2023-12-25 18:01:20
1710
发布2023-12-25 18:01:20
举报
文章被收录于专栏:CBeann的博客CBeann的博客

前提

(1)作者技术比较差,文章写的比较随意,也可能有错误,欢迎您指出。

(2)如果您不了解Spring Bean的声明周期,那么您可以看一下文章(Bean的生命周期_CBeann的博客-CSDN博客)或者百度其它文章,然后在回来看该文章,否则个人感觉应该看不懂

解决循环依赖

假设有一种下面的情况,A中有B,B中有A
代码语言:javascript
复制
@Data
public class A {
  private B b;
  public A() {System.out.println("A 无参构造器。。。");}
  public void speak() {System.out.println("------AAA---------");}
}

@Data
public class B {
  public B() {System.out.println("B 无参构造器。。。");}
  private A a;
  public void speak() {System.out.println("------BBB---------");}
}
图片分析
代码分析

创建的A的时候调用doCreateBean方法

1)调用A无参构造方法创建Bean

2)把该bean对象添加到三级缓存中(在下面的代码中有注释)

3)Bean的属性赋值,A的里面引用了B,所以此时会调用doCteateBean(B)

代码语言:javascript
复制
//AbstractAutowireCapableBeanFactory
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
			{

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			//省略
		}
		if (instanceWrapper == null) {
		    //1)调用无参构造方法创建Bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		
		synchronized (mbd.postProcessingLock) {
			//省略
		}

		
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				//省略
			}
			//2)把该bean对象添加到三级缓存中,注意getEarlyBeanReference方法,特别有用
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
		    //3)Bean的属性赋值
			populateBean(beanName, mbd, instanceWrapper);
			//4)处理aware接口、applyBeanPostProcessorsBeforeInitialization、initMethod
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				//省略
			}
			else {
				//省略
			}
		}

		if (earlySingletonExposure) {
			//省略
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			//省略
		}

		return exposedObject;
	}





}

此时在创建B的时候调用getBean(A),然后会走到下面代码的地方,从三级缓存中获取到A(B=null),返回该不完整的A的地址,然后B创建成功,然后继续创建A,然后A也创建成功。

-------------------------源码1

代码语言:javascript
复制
//DefaultSingletonBeanRegistry
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //从一级缓存中获取,即IOC容器,即完整的Bean对象
		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;
	}
循环依赖总结

(1)创建A的时候调用A的无参构造方法,然后在把得到的地址A(B=null)放入到三级缓存中,然后填充自己的属性B,也就会创建B;

(2)当创建B的时候,填充自己的属性A,从三级缓存中拿到A(B=null)地址,然后B创建成功;

(3)此时回到(1),此时拿到B,然后完善A,创建A成功。

(4)因为在(2)中拿到的是A的地址,所以在(3)中完善A在B中是一个。

三级缓存

疑问

个人感觉二级缓存足矣,为什么还要三级缓存?

反驳疑问

假设下面的场景:只有singletonObject(第一级缓存)和singletonFactory (第三级缓存),即没有earlySingletonObjects(第二级缓存)

如果有这么一种情况A(B),B(A),还有一个AOP是关注A的某个方法

此时的逻辑为:

1)创建A

2)把A(B=null)的地址(abc)存入singletonFactory缓存中

3)创建B

4)B在赋值a属性的时候,在singletonFactory缓存中拿出A的地址(abc)并且赋值给属性a(左边这句话是错的)(这就是三级缓存的关键),

4.1)没有AOP的时候,确实是存的a的地址,没错,返回的也是a的地址

4.2)如果有AOP,确实存进去的是a的地址,但是返回的已经不是A的地址了,是A的代理对象地址(看源码2,3,4)

总结:此时就出现问题了,如果没有earlySingletonObjects(第二级缓存),那么每次在singletonFactory (第三级缓存)中拿到的A对象都会创建创建一个代理对象,即每次向依赖A的对象中赋的值都是不同的代理对象,那么就不符合单例模式了。

-------------------------源码2

代码语言:javascript
复制
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;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

-------------------------源码3

代码语言:javascript
复制
//AbstractAutoProxyCreator
@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
        //跟进去
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

-------------------------源码4

代码语言:javascript
复制
//AbstractAutoProxyCreator
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;
		}

		// Create proxy if we have 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;
	}

总结

1)在没有AOP的情况下二级缓存足矣解决循环依赖,三级缓存更能解决问题。

2)三级缓存其实也是解决循环依赖的,是解决带AOP的循环依赖的,如上文中举的例子。如果您查的三级缓存资料没有说AOP,个人感觉这篇文章写的不是很充实。

本文没有回答的疑问

疑问1

上问中反驳二级缓存不能解决带AOP的循环依赖问题时,是把earlySingletonObjects(第二级缓存)去掉;如果我说我去掉singletonFactory (第三级缓存),那该如何反驳二级缓存不能解决带AOP的循环依赖问题呢???

疑问2

就拿上问中举的例字来说,A依赖B,B依赖A,有一个关注A的AOP。

下面是创建Bean声明周期的一段代码,以创建A为例

代码语言:javascript
复制
//AbstractAutowireCapableBeanFactory
protected Object doCreateBean{
//创建A
Object exposedObject = bean;
		try {
            //初始化A,因为A中有属性B,此时去创建B,然后把A的代理对象存入earlySingletonObjects缓存中,B创建完毕,然后又回到此处继续初始化A
			populateBean(beanName, mbd, instanceWrapper);
            //为非代理对象A执行aware接口等等
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			//省略
			}
		}

		if (earlySingletonExposure) {
            //在earlySingletonObjects中拿到代理对象A
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
                    //把exposedObject由指向非代理对象A变为指向代理对象A,那么
                    //exposedObject = initializeBean(beanName, exposedObject, mbd);
                    //我认为是白做了,我不清楚这个地方???????????????
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					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.");
					}
				}
			}

}
如果有知道上面两个问题答案的,可以在下问中评论,一起学习,共同进步
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前提
  • 解决循环依赖
    • 假设有一种下面的情况,A中有B,B中有A
      • 图片分析
        • 代码分析
          • 循环依赖总结
          • 三级缓存
            • 疑问
              • 反驳疑问
              • 总结
              • 本文没有回答的疑问
                • 疑问1
                  • 疑问2
                    • 如果有知道上面两个问题答案的,可以在下问中评论,一起学习,共同进步
                    相关产品与服务
                    容器服务
                    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档