首页 > 编程语言 >Spring源码-Spring为什么使用三级缓存解决循环依赖

Spring源码-Spring为什么使用三级缓存解决循环依赖

时间:2022-10-05 22:13:28浏览次数:55  
标签:缓存 String mbd Spring beanName Object bean 源码 singletonObject

Spring使用三级缓存,分别是singletonObjects,earlySingletonObjects,singletonFactories来解决循环依赖问题。但是用二级缓存就可以解决循环依赖了。为什么要使用三级缓存呢?因为有动态代理。必须保证单例bean在bean工厂中只有一个对象。所以提供了singletonFactories用lamada表达式用来将代理后的对象替换被代理对象。保证有动态代理bean工厂也只有一个单例bean。

一、修改为二级缓存

修改DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference),注释原来的这个函数,复制一份改为:

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

在DefaultSingletonBeanRegistry类新增以下方法:

protected void addearlySingletonObjects(String beanName, Object object) {
	Assert.notNull(object, "Singleton bean must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.earlySingletonObjects.put(beanName,object);
			this.registeredSingletons.add(beanName);
		}
	}
}

注释AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法里面的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));这行代码,在下面新增:

addearlySingletonObjects(beanName,bean);

在没有动态代理时可验证可解决循环依赖。

现在加上动态代理:

Logger.java

public class Logger {

public void dothing() {
	System.out.println("现在时间是:"+ DateFormat.getDateInstance().format(new Date()));
}
}

circle.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">


	<bean id="a" class="circle.A">
		<property name="b" ref="b"/>
	</bean>


	<bean id="b" class="circle.B">
		<property name="a" ref="a"/>
	</bean>

	<bean id="logger" class="circle.Logger"/>

	<aop:config>
		<aop:aspect id="logger" ref="logger">
			<aop:pointcut expression="execution(* circle.*.*(..))" id="method" />
			<aop:before method="dothing" pointcut-ref="method" />
		</aop:aspect>
	</aop:config>
</beans>

调试可知:

新建a,调用AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)调用createBeanInstance(beanName, mbd, args)实例化a,调用populateBean(beanName, mbd, instanceWrapper)填充a的属性b,populateBean调用applyPropertyValues(beanName, mbd, bw, pvs)填充属性,applyPropertyValues又调用valueResolver.resolveValueIfNecessary(pv, originalValue)解析获取b,resolveValueIfNecessary通过resolveReference,resolveReference通过beanFactory.getBean(resolvedName)获取b,经过b的实例化和从earlySingletonObjects中获取属性a完成了b的创建和属性填充。在调用populateBean后,调用initializeBean(beanName, exposedObject, mbd)生成了b的代理对象并在DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)通过addSingleton(String beanName, Object singletonObject)将b放入singletonObjects
。返回填充a的属性b后:

通过initializeBean(beanName, exposedObject, mbd)生成a的代理,而earlySingletonObjects保存的a是A@2397,即bean,生成a的代理对象是circle.A@769a1df5,即exposedObject,AbstractAutowireCapableBeanFactory.doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)调用

	if (earlySingletonExposure) {
		Object earlySingletonReference = getSingleton(beanName, false);
		if (earlySingletonReference != null) {
			if (exposedObject == bean) {
				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 " +
							"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}

导致exposedObject == bean为false,有两个不同的bean而抛出无法解决循环依赖的异常。

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] 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.

标签:缓存,String,mbd,Spring,beanName,Object,bean,源码,singletonObject
From: https://www.cnblogs.com/shigongp/p/16756416.html

相关文章

  • SpringBoot简介入门
                   ......
  • 119-23-ZooKeeper FastLeaderElection 选举源码剖析_ev
            quorumpeer.start()方法启动涉及目录上面的6件事                           ......
  • SpringCloud Gateway API接口加解密
    接口范围所有GET请求白名单除外body体是application_json和application_json_utf8的POST请求白名单除外POSTurl传参也支持白名单除外启用禁用/版本后端提......
  • can-utils源码解析cansend
    前言本文主要介绍socketCan中的发送函数cansend的源码解析.代码/**cansend.c-simplecommandlinetooltosendCAN-framesviaCAN_RAWsockets**Copyright(c)20......
  • 【SpringCloud初始】之父工程pom文件
    前面,我们已经学习了创建SpringCloud的第一步新建父工程。接下来,我们需要对pom文件进行修改。环境IDEA2018.2.4JDK1.8.0_191Maven3.5.2步骤新建父工程后。删除src......
  • Spring Cloud:第四章:Hystrix断路器
    Hystrix“豪猪”,具有自我保护的能力。hystrix通过如下机制来解决雪崩效应问题。资源隔离:包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现......
  • Spring Boot 快速图片上传封装
    importcom.alibaba.fastjson.JSONObject;importcom.retailo2o.server.domain.base.ResponseData;importio.swagger.annotations.Api;importorg.slf4j.Logger;impo......
  • Spring Cloud:第三章:Ribbon客服端负载均衡
    负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段。理解Ribbon对于我们使用SpringCloud来讲非常的重要。它是一个基于Http和TCP的客户端负载均衡工具。......
  • 记一次SpringBoot中跨域的小问题
    记一次SpringBoot中跨域的小问题问题前阵子,有个学长在跨域的时候遇到一个问题,我们两个人互相讨论了一番,得到了问题的答案。问题如下:如果按照上图的方式配置跨域类,那么......
  • java中的自动拆装箱与缓存(Java核心技术阅读笔记)
    最近在读《深入理解java核心技术》,对于里面比较重要的知识点做一个记录!众所周知,Java是一个面向对象的语言,而java中的基本数据类型却不是面向对象的!为了解决这个问题,Java为......