IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring系列(四) 循环依赖那些事 -> 正文阅读

[Java知识库]Spring系列(四) 循环依赖那些事

随着我们代码中更多服务的出现与依赖关系的耦合,bean与bean之间的交互往往会涉及很多对其他类的依赖。我们通常用简单的Autowired、提供对应依赖对象的setter方法以及构造器中传入需要引用的其他对象,spring就可以完美的做好依赖关系自动装配。那么本节就来分析下spring中是如何完成bean之间这些依赖的自动查找与注入

关于这个话题,最重要的是spring中用到的三级缓存。但spring中的循环依赖情况分很多种,本文会进行不同场景的依次分析。

一级缓存:singletonObjects (存放的是已经完成整个生命周期初始化的对象,是正常getBean方式拿出来的源头)

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存:earlySingletonObjects (暂时放的是通过各种方式实例化出来的对象,属性及初始化方法及后置处理等过程还未进行)

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存:singletonFactories (允许提前暴露引用下用于创建bean的工厂,在代码体现上是一个lamda方法:()->createBean(beanName, mbd, args))

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

单例与单例setter依赖

在spring中,创建的bean如果不指定scope一般默认为单例的。注解依赖方式包括字段级别注解及方法级别注解两种情况,常见A,B依赖代码如下

Bean1:

@Component
public class BeanSetter1 {
    @Autowired
    private BeanSetter2 beanSetter2;
     public void print(){
        System.out.println("autowired beanSetter2:"+beanSetter2);
    }
//@Autowired
//    public void setBeanSetter2(BeanSetter2 beanSetter2) {
 //       this.beanSetter2 = beanSetter2;
  //  }
//}

Bean2:

@Component
public class BeanSetter2 {
    @Autowired
    private BeanSetter1 beanSetter1;
    public void print(){
        System.out.println("autowired beanSetter1:"+beanSetter1);
    }
 //   @Autowired
//    public void setBeanSetter1(BeanSetter1 beanSetter1){
//        this.beanSetter1 = beanSetter1;
//    }
}

启动容器,通过context拿到对应的bean以及其中注入的依赖。其中BeanSetter1与BeanSetter2中的属性都与容器中各自对应的beanSetter是同一个单例对象
在这里插入图片描述
对于singleton类型的bean,spring在创建context时会完成对该bean的实例化、初始化、后置处理等一系列过程。在refresh中的finishBeanFactoryInitialization中就是负责对所有满足条件的bean进行预先创建及缓存过程。
判断条件如下: 非抽象、单例、非懒加载。对FactoryBean还需要满足允许提早初始化
在这里插入图片描述
在上面流程中,当beanName为beanSetter1时,getBean() 开始进行BeanSetter1的创建及初始化

缓存获取

第一次从singletonObjects缓存中拿,同时允许查找提前引用缓存allowEarlyReference->singletonFactories
DefaultSingletonBeanRegistry

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

可惜现在bean才开始创建并不在缓存中也还未设置开始创建等状态,所以拿出来为空直接返回。接着为创建该bean做一些bd的清除及状态设定
markBeanAsCreated

clearMergedBeanDefinition(beanName)
this.alreadyCreated.add(beanName);

按照bean的scope类型进行不同方式的创建

if (mbd.isSingleton()) {
   sharedInstance = getSingleton(beanName, () -> {
						...
							return createBean(beanName, mbd, args);
						...
					);
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

创建bean的开始

在getSingleton中,第二次尝试从singletonObjects中拿依然为空,此时经过bean创建前期的一系列检验及状态的标记后调用ObjectFactory进行bean的真正创建即createBean(beanName, mbd, args)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException...
				}
				//检验当前bean是否可以创建的:不在inCreationCheckExclusions名单中且成功加入singletonsCurrentlyInCreation中的。(在ConfigurationClassEnhancer中会通过setCurrentlyInCreation对该两种名单设置)
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				try {
				//真正完成bean创建、初始化、后置处理及代理替换的入口
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
				...
				}
				catch (BeanCreationException ex) {
					...
				}
				finally {
					...
					//不在inCreationCheckExclusions名单中并从在创建bean名单中移除
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
			//将完成所有过程的bean放置到一级缓存singletonObjects,并从其他缓存中移除
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
}

对于objectFactory创建bean的过程进行分析,
AbstractAutowireCapableBeanFactory

	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
	//1.解析beanClass,找到需要创建的类型是什么
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
//2.进行方法override方法的查找与设置
mbdToUse.prepareMethodOverrides();
//3.调用当前容器中是否有后置处理器InstantiationAwareBeanPostProcessor的applyBeanPostProcessorsBeforeInstantiation能提前得到对象(并一起完成applyBeanPostProcessorsAfterInitialization)提前结束,一般用于给使用者通过配置或者自定义获得一个代理对象(大部分情况都是null)
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
	return bean;
}
}
//4. 进行具体创建bean的流程处理
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance 

接下来分析doCreateBean的实现过程
AbstractAutowireCapableBeanFactory

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
//1.从factoryBeanMethodCache中拿或者创建一个,主要是factoryBean情况下
BeanWrapper instanceWrapper =  this.factoryBeanInstanceCache.remove(beanName)
or 
//其中允许提供自定义构造方式obtainFromSupplier、factoryMethodName实例化,构造参数注入及默认实例化等方式直接创建。
//在@Configuration中使用@Bean方式下定义的bean创建可以用factoryMethod方式创建
//有参构造函数且参数必须时,autowireConstructor创建
//其他情况采用默认方式instantiateBean即class默认构造函数实例化
createBeanInstance(beanName, mbd, args)
}
//2.在之前的文章中讲到这里会对MergedBeanDefinitionPostProcessor进行postProcessMergedBeanDefinition
//对于@Autowired会进行属性设置处理逻辑的缓存InjectionMetadata
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
//3.判断是否需要提前暴露:单例且允许循环引用且当前bean在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
//加入工厂方法getEarlyBeanReference(beanName, mbd, bean)到三级缓存singletonFactories中
//移除earlySingletonObjects中对应对象如果存在有的情况(执行到这里实际列表是空的)
//加入registeredSingletons单例注册表中	
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
//4.其中完成对属性的注入填充,在设置属性之前会尝试找合适的InstantiationAwareBeanPostProcessor能否进行bean的特殊处理提前结束(可以用来自定义扩展生成代理)。
//没有特殊处理则进行正常属性的设置,通过所有InstantiationAwareBeanPostProcessor的postProcessProperties提供属性的具体对象查找,包括之前以Auowired方式或setter方法进行的缓存。
populateBean(beanName, mbd, instanceWrapper);
//后续过程主要是初始化方法及初始前后置处理等,可参照之前的文章
exposedObject = initializeBean(beanName, exposedObject, mbd);
...

属性的填充与注入

接上面populateBean的内容,针对测试代码这里是由AutowiredAnnotationPostProcessor起作用完成对BeanSetter1中属性对象beanSetter2的查找
InjectionMetadata:
在这里插入图片描述
由于我们在属性上设置的Autowired注解,由AutowiredFieldElement负责处理(若在方法上对应AutowiredMethodElement,最后通过方法反射注入值)

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
//1.拿到对应要填充的类字段
Field field = (Field) this.member;
//2.尝试缓存加载
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}else{
//构造依赖描述符信息
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
//核心逻辑,是从beanFactory查找对应类型的bean是否存在并返回
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

//缓存该注入类型及结果
if (!this.cached) {
if (value != null || this.required) {
	this.cachedFieldValue = desc;
	registerDependentBeans(beanName, autowiredBeanNames);
    if (autowiredBeanNames.size() == 1) {
							...
								{
	this.cachedFieldValue = new ShortcutDependencyDescriptor(										desc, autowiredBeanName, field.getType());
								}
							}
						}
						else {
							this.cachedFieldValue = null;
						}
						this.cached = true;
					}
}

//调用反射方式直接注入查找到的类型bean
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}

属性对象的推断

从beanFactory中查找合适的bean完成属性对象的创建或查找
DefaultListableBeanFactory

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//对一些特殊的属性类型判读是否要特殊方式处理,如Optional,ObjectFactory等
...		
//如果这里有懒加载等注解存在,则会buildLazyResolutionProxy进行代理创建(只有在真正使用访问到该属性时才会从容器中查找,一般都是在容器创建完成后去调用才会发生这样的操作),否则直接null结束
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
if (result == null) {
//对于非懒加载的情况,会继续进行依赖的查找
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}

非懒加载继续解析查找

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
//ContextAnnotationAutowireCandidateResolver继承QualifierAnnotationAutowireCandidateResolver的解析方法,获取到value为null
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
...
//这里从beanFactory(包括parentBeanFactory)中找到合适的bean对象
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

if (matchingBeans.isEmpty()) {
//如果没查找到,并且required时需要抛出exception,否则直接以null返回,未有合适的bean可以被注入
  ...
}
//如果找到超过一个,进行最优选择根据Primary信息、对象实现的优先级等(determinePrimaryCandidate及determineHighestPriorityCandidate)决定一个最合适的bean
if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
}else{
//存在刚好只有一个的情况,直接用第一个作为要查找的beanName
}
//拿到的instanceCandidate 是BeanSetter2的class对象,通过beanFactory.getBean(autowiredBeanName)进行依赖对象真正bean的创建
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
//得到在spring容器中依赖对象的bean
Object result = instanceCandidate;
...
return result;
}

findAutowireCandidates中,从容器中查找出所有合适类型的beanName,根据具体类型进行筛选决定

protected Map<String, Object> findAutowireCandidates(
			@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
//所有对应合适类型的beanName,主要是从beanDefinitionNames中查找该信息;
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
//如果注入的类型是特殊类型如ApplicationContext,ApplicationEventPublisher等及子类,尝试获取并设置
result.put(key,value)
}
//对先查找的bean进行检验是否可以被作为注入的选择,主要是BeanDefinition中的isAutowireCandidate属性决定(一般默认为true)
...
for (String candidate : candidateNames) {
  if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
  //将候选bean的相关信息加入到当前结果result中
     addCandidateEntry(result, candidate, descriptor, requiredType);
   }
}
return result;
}

resolvableDependencies列举情况
在这里插入图片描述

属性类型的bean创建

在DependencyDescriptor中开始依赖对象的bean查找或创建过程。
此时beanSetter1还处于populateBean阶段,两者在各级缓存中的存在情况如下

singletonObjectsearlySingletonObjectssingletonFactories
beanSetter1NNY
beanSetter2NNN

在前面doResolveDependency中,拿出的instanceCandidate 如果为class类型的对象(还未在缓存中的情况),则需要从beanFactory中拿或者创建依赖的bean实例

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {
		return beanFactory.getBean(beanName);
	}

同BeanSetter1的创建过程一样,BeanSetter2的过程中也允许提早暴露自己的对象工厂,会在singletonFactories中加入自己的创建对象方法,此时singletonFactories 存在了两个对象的工厂创建方法。
之后在populateBean时会对依赖属性beanSetter1进行属性的注入查找,
通过beanFactory查找beanSetter1时,会尝试从各级缓存中拿,最终会通过beanSetter1对应的对象工厂创建,得到实例对象后从工厂缓存singletonFactories移除并把实例加到二级缓存earlySingletonObjects
在这里插入图片描述

singletonObjectsearlySingletonObjectssingletonFactories
beanSetter1NYN
beanSetter2NNY

在getEarlyBeanReference中,这里传入的bean就是还处在populateBean阶段的BeanSetter1对象的引用,通过各个SmartInstantiationAwareBeanPostProcessor后置处理器的调用拿到最终的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;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

至此,beanSetter2对象的依赖属性beanSetter1已经得到值(只是这个值是实例化出来的简单对象,但指向同一个beanSetter1引用)并返回,继续进行beanSetter2在populateBean阶段的postProcessProperties位置,开始执行后续初始化方法直到整个bean创建完成加入到一级缓存singletonObjects中并返回,标志该bean在spring中创建的完成,可从容器中取出正常使用。

singletonObjectsearlySingletonObjectssingletonFactories
beanSetter1NYN
beanSetter2YNN

属性填充后续初始化

回到beanSetter1对象的创建,在完成对依赖beanSetter2对象的查找中促使了在容器中的完整创建,现在beanSetter1已经完成了properties的处理填充过程,对应的字段也被spring接入了在容器中合适的对象类型的bean。从populateBean开始继续完成接下来的初始化及后置初始化处理等过程,经过最后一次可能进行代理的后置过程后,beanSetter1进行提早引用的检查是否当前对象被代理更改过

AbstractAutowireCapableBeanFactory
doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
...
Object exposedObject = bean;
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
...
if (earlySingletonExposure) {
//从二级缓存中拿出当前该bean的实例对象,不再从singletonFactories中查找。如果能拿到则代表该bean有可能被其他bean进行依赖,像beanSetter1从三级缓存到二级缓存的过程是因为被beanSetter2的依赖引用导致,而beanSetter2未被任何其他对象在属性上引用,则根本不会存在二级缓存中
Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
//存在时则进行当前可能经过代理逻辑调用之后得到的对象与当前在创建的对象比较,看是否会有引用关系不一致问题
//因为beanSetter2引用的依赖对象是此时在二级缓存中的beanSetter1对象即earlySingletonReference,如果在beanSetter1的后置初始化过程中有代理逻辑等发生使得当前beanSetter1对象即exposedObject 不与原来createBeanInstance实例化出来的对象类型一致则有可能出现引用不一致问题
				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...
					}
				}
			}
		}
...		
}		

等校验通过后,完成接下来的创建并加入到singletonObjects中,循环依赖结束。

单例与单例构造依赖

在很多场景我们需要直接通过构造方式直接传入强依赖的对象,比如下面构造互相引用代码

非懒加载

BeanCtr1

@Component
public class BeanCtr1 {
    private BeanCtr2 beanCtr2;
    public BeanCtr1(BeanCtr2 beanCtr2){
        this.beanCtr2 = beanCtr2;
    }
    public void print(){
        System.out.println("BeanCtr1 print beanCtr2:"+beanCtr2);
    }
}

BeanCtr2

@Component
public class BeanCtr2 {
    private BeanCtr1 beanCtr1;
    public BeanCtr2(BeanCtr1 beanCtr1) {
        this.beanCtr1 = beanCtr1;
    }
    public void print(){
        System.out.println("BeanCtr2 print beanCtr1:"+beanCtr1);
    }
}

这样的循环引用可以吗?不行,因为beanCtr1在通过构造方式获得beanInstance查找构造器参数的过程与前面类似(没有懒加载的情况会一直继续直到找出实例为止),最终从当前beanDefinitionMap中找到一个合适类型对应的bean信息即beanCtr2,通过beanFactory的getBean去查找或创建。这就
就要求beanCtr2至少需要在二级缓存中能够暂时被引用。而之前分析过,只有在bean实例化出来后有了BeanWrapper,后续在允许提前引用时才有机会被放到三级缓存singletonFactories中,之后被依赖对象查找时再提升到二级缓存。而被依赖的beanCtr2的创建过程也需要非懒加载的beanCtr1的实例,此时创建beanSetter1因为它已经在创建过程中了,再次标记创建中将会抛出exception

详细过程如下图整理所示
在这里插入图片描述

因此如果要用构造器互相依赖成功,需要将构造方法标志为Lazy加载。

懒加载

只需要在先初始化的BeanCtr1构造方法上用Lazy注解,在创建BeanCtr1的构造参数依赖时,会将依赖BeanCtr2作为proxy而不再从beanFactory中查找或创建对应BeanCtr2的实例,继续完成之后的初始化等直到加入一级缓存中结束。对BeanCtr2的构造参数非懒加载BeanCtr1,进行正常beanFactory.getBean就已经能找到之前已初始化完在singletonObjetcs中的BeanCtr1。

从上图懒加载过程图中,先构造懒加载proxy
buildLazyResolutionProxy时设置具体获取target的逻辑TargetSource到ProxyFactory中,内容为从beanFactory根据依赖描述符解析获取bean。

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
		...
		final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

		TargetSource ts = new TargetSource() {
			@Override
			public Class<?> getTargetClass() {
				return descriptor.getDependencyType();
			}
			@Override
			public boolean isStatic() {
				return false;
			}
			@Override
			public Object getTarget() {
				Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
				Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
				if (target == null) {
					Class<?> type = getTargetClass();
// 根据类型作支持类型如Map,List,Set等的默认返回或throw exception
					...
				
				if (autowiredBeanNames != null) {
					for (String autowiredBeanName : autowiredBeanNames) {
						if (dlbf.containsBean(autowiredBeanName)) {
							dlbf.registerDependentBean(autowiredBeanName, beanName);
						}
					}
				}
				return target;
			}
			@Override
			public void releaseTarget(Object target) {
			}
		};

		ProxyFactory pf = new ProxyFactory();
		pf.setTargetSource(ts);
     	...
		return pf.getProxy(dlbf.getBeanClassLoader());
	}

在创建proxy中根据config创建合适代理对象
在这里插入图片描述

此时config的信息满足hasNoUserSuppliedProxyInterfaces,再根据接口或proxy类型判断用JDK还是cglib代理
optimize:false
proxyTargetClass:false
interfaces:empty
这里情况将会调用ObjenesisCglibAopProxy进行代理,CglibAopProxy 获得proxy时根据exposeProxy及静态方法类型与否创建如下表的拦截执行方式,去执行真正targetSource中getTarget方法获取对象。
如果代理方法为static,实际上在此时已经直接获取targetSource中的调用结果及真正的目标对象。

staticnon static
exposeProxyStaticUnadvisedExposedInterceptorDynamicUnadvisedExposedInterceptor
not exposeProxyStaticUnadvisedInterceptorDynamicUnadvisedInterceptor

拿到懒加载的代理对象后,当我们需要访问其中的依赖对象时会触发当时的targetSource中的getTarget方法,去从bean中查找对应的依赖bean
当调用beanCtr1的print方法时,

BeanCtr1 beanCtr1 = context.getBean(BeanCtr1.class);
System.out.println(beanCtr1);
beanCtr1.print();
BeanCtr2 beanCtr2 = context.getBean(BeanCtr2.class);
System.out.println(beanCtr2);
beanCtr2.print();
//打印结果如下
com.example.spring5.BeanCtr1@740fb309
BeanCtr1 print beanCtr2:com.example.spring5.BeanCtr2@5f8e8a9d
com.example.spring5.BeanCtr2@5f8e8a9d
BeanCtr2 print beanCtr1:com.example.spring5.BeanCtr1@740fb3

触发了beanCtr2的toString方法,懒加载的逻辑此时会最终调用从beanFactory中获取beanCtr2单例对象
在这里插入图片描述

单例与原型setter依赖

单例与原型的循环依赖可以成功,但根据原型的定义beanSingleton中的引用对象每次拿出来的都不会是同一个原型对象实例。原型对象的实例只会在调用或访问的时候才会创建,不可能出现在三级缓存中任何一个地方

如下代码
BeanPrototype :

@Component
@Scope("prototype")
public class BeanPrototype {
    @Autowired
    private BeanSingleton beanSingleton;
    public void print(){
        System.out.println("BeanPrototype print beanSingleton:"+beanSingleton);
    }
}

BeanSingleton:

@Component
public class BeanSingleton {
    @Autowired
    private BeanPrototype beanPrototype;
    public void print(){
        System.out.println("BeanSingleton print beanPrototype:"+beanPrototype);
    }
}

在初始化BeanPrototype时,由于是原型不属于之前提到的提早初始化条件,不会进行创建因此在容器缓存中没有相关的信息。

!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()

在初始化BeanSingleton时,与之前单例bean过程一样,会将对应创建方法存在三级缓存singletonFactories中,之后在属性设值阶段populateBean会触发对依赖对象BeanPrototype实例的创建,根据容器中beanDefinition的信息进行bean的创建
BeanPrototype被动创建时,不满足earlySingletonExposure情况因此不会加入singletonFactories缓存中自己的工厂方法,执行到对beanSingleton属性的解析时,通过beanFactory.getBean(beanName),按顺序从缓存中拿到之前的创建对象工厂方法获得实例并赋值给属性字段。原型对象的创建接下来正常执行完成,但不会加入到singletonObjects一级缓存中。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 此时beanName为beanSingleton,allowEarlyReference为true
		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);
						if (singletonObject == null) {
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
           //最终拿出对应的创建方法并获得实例存入二级缓存中							
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

BeanSingleton依赖的对象beanPrototype解析成功后,继续完成单例的剩余初始化过程,直到加入singletonObjects结束。

原型与原型setter依赖

不可以依赖成功,因为原型会因为解决属性依赖时被检测为并发标记创建中,抛出Exception。
测试代码
BeanPrototype1

@Component
@Scope("prototype")
public class BeanPrototype1 {
    @Autowired
    private BeanPrototype2 beanPrototype2;
    public void print(){
        System.out.println("BeanPrototype1 print beanPrototype2:"+beanPrototype2);
    }
}

BeanPrototype2

@Component
@Scope("prototype")
public class BeanPrototype2 {
    @Autowired
    private BeanPrototype1 beanPrototype1;
    public void print(){
        System.out.println("BeanPrototype2 print beanPrototype1:"+beanPrototype1);
    }
}

在容器初始化后getq其中一个bean时就会提示你循环依赖错误问题。
主要报错来自如下过程
AbstractBeanFactory

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
Object sharedInstance = getSingleton(beanName);
//能获取到缓存对象时
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
//还未在缓存中时,如果已经在创建中但因为被别的对象依赖引用该原型再次触发创建执行到这个地方,那么就造成了原型的循环创建
if (isPrototypeCurrentlyInCreation(beanName)) {
	throw new BeanCurrentlyInCreationException(beanName);
}
//按类型进行各自对象的创建
if(singleton){
sharedInstance = getSingleton(beanName, () -> {
							return createBean(beanName, mbd, args);});
...							
}else if(prototype){
//标记为原型正在创建状态
beforePrototypeCreation(beanName);
//创建原型对象
prototypeInstance = createBean(beanName, mbd, args);
//清除状态
afterPrototypeCreation(beanName);
...
}

}

在spring中循环依赖的常见情况已经有了明确的认识后,在之后的使用中就可以正确设置好一些依赖关系避免程序的意外错误。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:31:00  更:2021-08-23 16:31:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 2:01:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码