spring循环依赖

Posted 莫名的猪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring循环依赖相关的知识,希望对你有一定的参考价值。

spring循环依赖主要有三种:

        单例引用类型循环依赖(属性):允许

        构造器的循环依赖:不允许

        多例循环依赖:不允许

单例引用类型循环依赖(属性)

package com.spring.bean;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Data
@Component
public class CirculeA 
    @Autowired
    private CirculeB circuleB;//引用CirculeB

package com.spring.bean;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Data
@Component
public class CirculeB 

    @Autowired
    private CirculeA circuleA;//引用CirculeA 

 @org.junit.Test
    public void test3()
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        System.out.println("spring启动成功");
    

总结:在circuleA实例化过程中触发circuleB的getBean(),此时circileA的实例已放入到三级缓存中,在circuleB的实例化过程中会触发circuleA的genBean(),直接从缓存中拿到circileA的实例,这样会优先将circuleB是实例化完成,并在circuleA触发circuleB的getBean()时返回,然后继续完成circuleA的实例化;

circuleA第一次实例化会走以下代码,第二次直接从缓存中获取不会走以下代码

 

    单例实例化流程图: 

构造器的循环依赖:  

package com.spring.bean;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Data
@Component
public class CirculeB 

    private CirculeA circuleA;

    public CirculeB(CirculeA circuleA) 
        this.circuleA = circuleA;
    

package com.spring.bean;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Data
@Component
public class CirculeA 

    private CirculeB circuleB;

    public CirculeA(CirculeB circuleB) 
        this.circuleB = circuleB;
    

报错:org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'circuleA' defined in file [D:\\XXX\\5.2.8\\spring_demo\\spring_test\\target\\classes\\com\\spring\\bean\\CirculeA.class]: 
Unsatisfied dependency expressed through constructor parameter 0; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'circuleB' defined in file [D:\\xxxx\\5.2.8\\spring_demo\\spring_test\\target\\classes\\com\\spring\\bean\\CirculeB.class]:
 Unsatisfied dependency expressed through constructor parameter 0; 
 nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
 Error creating bean with name 'circuleA': Requested bean is currently in creation: Is there an unresolvable circular reference?

        //创建实例,在这个方法中触发circuleB的getBean()
		if (instanceWrapper == null) 
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) 
			mbd.resolvedTargetType = beanType;
		

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) 
			if (!mbd.postProcessed) 
				try 
					//AutowiredAnnotationBeanPostProcessor 收集有@Autowire和@Value注解的方法和属性,
					// 放入到injectionMetadataCache缓存中,包装为InjectionMetadata.InjectedElement对象,其中有member,isFiled属性相对重要
					//CommonAnnotationBeanPostProcessor 收集有@PostConstruct和@PreDestroy注解的方法 放入到lifecycleMetadataCache,
					// 有@Resource注解的方法和属性 放入到injectionMetadataCache缓存中,
					// 包装为InjectionMetadata.InjectedElement对象,其中有member,isFiled属性相对重要
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				
				catch (Throwable ex) 
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				
				mbd.postProcessed = true;
			
		

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		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));
		

总结:在放入缓存前调用getBean(),导致缓存中没有,所以每次调用getbean()都会走beforeSingletonCreation()方法,在第二次调用时判断以下条件时(!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName))返回true,会抛出异常

多例循环依赖:

package com.spring.bean;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Data
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CirculeB 

    @Autowired
    private CirculeA circuleA;

package com.spring.bean;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Data
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CirculeA 
    @Autowired
    private CirculeB circuleB;


 @org.junit.Test
    public void test4()
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        CirculeA bean = context.getBean(CirculeA.class);
        CirculeB bean1 = context.getBean(CirculeB.class);
        System.out.println("spring启动成功");
    

会报错:org.springframework.beans.factory.UnsatisfiedDependencyException:
 Error creating bean with name 'circuleA': Unsatisfied dependency expressed through field 'circuleB'; 
 nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
 Error creating bean with name 'circuleB': Unsatisfied dependency expressed through field 'circuleA'; 
 nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
 Error creating bean with name 'circuleA': Requested bean is currently in creation: Is there an unresolvable circular reference?

原因:第一次调用会在ThreadLocal中存放,在第二次调用以下方法时抛出异常

Spring的循环依赖问题

spring容器循环依赖包括构造器循环依赖和setter循环依赖,那Spring容器如何解决循环依赖呢?首先让我们来定义循环引用类:

在Spring中将循环依赖的处理分成了3种情况:

  1. 构造器循环依赖
  2. setter循环依赖
  3. prototype范围的依赖处理

构造器循环依赖

通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。

如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建TestC,最终在创建TestC时发现又需要TestA,从而形成一个环,没办法创建。

Spring容器将每一个正在创建的bean标识符放在一个"当前创建bean池"中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建bean过程中发现自己已经在"当前创建bean池"里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从"当前创建bean池"中清除掉。

setter循环依赖

通过setter注入方式构成的循环依赖。对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean。

  1. Spring容器创建单例"testA"bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将"testA"标识符放到"当前创建bean池",然后进行setter注入"testB"。
  2. Spring容器创建单例"testB"bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将"testB"标识符放到"当前创建bean池",然后进行setter注入"testC"。
  3. Spring容器创建单例"testC"bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返回一个提前暴露一个创建中的bean,并将"testC"标识符放到"当前创建bean池",然后进行setter注入"testA"。进行注入"testA"时由于提前暴露了"ObjectFactory"工厂,从而使用它返回提前暴露一个创建中的bean。
  4. 最后再依赖注入"testB"和"testA",完成setter注入。

prototype范围的依赖处理

对于"prototype"作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存"prototype"作用域的bean,因此无法提前暴露一个创建中的bean。

对于"singleton"作用域bean,可以通过"setAllowCircularReferences(false);"来禁用循环引用。

 

以上是关于spring循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

手撕Spring源码,详细理解Spring循环依赖及三级缓存

深谈Spring如何解决Bean的循环依赖