案例:

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B又依赖于A。如下图:

springboot关闭循环依赖检查 spring如何解决循环依赖的_初始化


“依赖”在Spring中有两种情况:

1.构造器循环依赖

2.field属性注入循环依赖

构造器循环依赖

@Service
public class A {  
    public A(B b) {  }
}

@Service
public class B {  
    public B(A a) {  
    }
}

此时启动项目会报错

field属性注入循环依赖

@Service
public class A1 {  
    @Autowired  
    private B1 b1;
}

@Service
public class B1 {  
    @Autowired  
    public A1 a1;
}

此时项目可以正常启动

field属性注入循环依赖(prototype)

@Service
@Scope("prototype")
public class A1 {  
    @Autowired  
    private B1 b1;
}

@Service
@Scope("prototype")
public class B1 {  
    @Autowired  
    public A1 a1;
}

此时启动项目会报错

结论:
1.基于构造函数的注入,如果有循环依赖,Spring是不能够解决的
2.基于prototype类型的属性注入,如果有循环依赖,Spring是不能够解决的
3.@Service默认是单例的,如果有循环依赖,Spring可以解决

源码解析

SpringBean的加载入口
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
applicationContext.getBean(A.class);

了解:
ClassPathXmlApplicationContext :加载XML配置文件
AnnotationConfigWebApplicationContext : 加载Scan注解

此时第二行已经可以获取到bean示例了,即表示在第一行已经完成了对所有bean的加载

ClassPathXmlApplicationContext 构造方法
public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		super(parent);
		//  存储spring配置文件到本地
		setConfigLocations(configLocations);
		if (refresh) {
			//  spring加载bean的核心方法,刷新spring整个的上下文信息
			refresh();
		}
	}
refresh() 方法代码
@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			prepareRefresh();
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			prepareBeanFactory(beanFactory);
			try {
				// 设置BeanFactory的后置处理器
				postProcessBeanFactory(beanFactory);
				invokeBeanFactoryPostProcessors(beanFactory);
				registerBeanPostProcessors(beanFactory);
				
				initMessageSource();
				initApplicationEventMulticaster();.
				onRefresh();
				registerListeners();
				// 初始化所有的no-lazy-init  重要!!!
				finishBeanFactoryInitialization(beanFactory);
				finishRefresh();
			} catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				destroyBeans();
				cancelRefresh(ex);
				throw ex;
			} finally {
				resetCommonCaches();
			}
		}
	}

obtainFreshBeanFactory 方法:获取刷新Spring上下文的Bean工厂

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		return beanFactory;
}
protected final void refreshBeanFactory() throws BeansException {  
    if (this.hasBeanFactory()) {    
        this.destroyBeans();    
        this.closeBeanFactory();  
    }  
    try {    
   		 // DefaultListableBeanFactory 重要!!!
        DefaultListableBeanFactory beanFactory = this.createBeanFactory();    
        beanFactory.setSerializationId(this.getId());    
        this.customizeBeanFactory(beanFactory);    
        this.loadBeanDefinitions(beanFactory);    
        synchronized(this.beanFactoryMonitor) {      
            this.beanFactory = beanFactory;    }  
        } catch (IOException var5) {    
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);  
        }
}
finishBeanFactoryInitialization 方法代码

此代码是 初始化所有的no-lazy-init 重要!!!

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}.
		beanFactory.setTempClassLoader(null);
		beanFactory.freezeConfiguration();
		//  初始化所有的单例bean
		beanFactory.preInstantiateSingletons();
	}
preInstantiateSingletons 方法部分代码

作用:初始化所有的单例bean

for (String beanName : beanNames) {
	RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
	if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
		if (isFactoryBean(beanName)) {
			// 实例化的核心代码 即getBean
			final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
			boolean isEagerInit;
			if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
				isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
						((SmartFactoryBean<?>) factory).isEagerInit(),
						getAccessControlContext());
			}
			else {
				isEagerInit = (factory instanceof SmartFactoryBean &&
						((SmartFactoryBean<?>) factory).isEagerInit());
			}
			if (isEagerInit) {
				getBean(beanName);
			}
		}
		else {
			getBean(beanName);
		}
	}
}

getBean方法:对于所有获取Bean对象是实例,都是用这个getBean方法,这个方法最终调用的是doGetBean(AbstractBeanFactory)方法,这个方法就是所谓的DI(依赖注入)发生的地方。

@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}
doGetBean 方法实现

首先先了解三个map

/**一级缓存,用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用*/
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

 /**三级缓存 存放 bean 工厂对象,用于解决循环依赖*/
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

 /**二级缓存 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖*/
 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

一级缓存:singletonObjects,存放完全实例化属性赋值完成的Bean,直接可以使用。
二级缓存:earlySingletonObjects,存放早期Bean的引用,尚未属性装配的Bean,提前曝光的单例对象;
三级缓存:singletonFactories,里面存放的是要被实例化的对象的对象工厂

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		final String beanName = transformedBeanName(name);
		Object bean;
		// 获取单例对象
		Object sharedInstance = getSingleton(beanName);

doGetBean 方法中首先根据 getSingleton()方法获取单例对象,但是第一次初始化的时候是没有单例对象的,获取为null。代码会继续向下走。

if (mbd.isSingleton()) {
	sharedInstance = getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			destroySingleton(beanName);
			throw ex;
		}
	});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

此时会调用createBean()方法去创建一个实例;

createBean 源码

@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		...
		try {
		// 调用此方法
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		return beanInstance;
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		...
}

AbstractAutowireCapableBeanFactory->doCreateBean 实例化之后,会将bean提早曝光

// 当bean为单例 && 容器配置允许循环依赖 && bean正在创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
	if (logger.isDebugEnabled()) {
		logger.debug("Eagerly caching bean '" + beanName +
				"' to allow for resolving potential circular references");
	}
	addSingletonFactory(beanName, new ObjectFactory<Object>() {
		@Override
		public Object getObject() throws BeansException {
		// getEarlyBeanReference走的是InstantiationAwareBeanPostProcessorAdapter->getEarlyBeanReference方法
// 点进去可以看到其实就是返回自己
			return getEarlyBeanReference(beanName, mbd, bean);
		}
	});
}

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)) {
			// 将bean加入三级缓存中
			this.singletonFactories.put(beanName, singletonFactory);
			// 将bean从二级缓存中移除
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

可以看出三级缓存跟二级缓存两个map其实是互斥的

此时A(未设置属性值)的引用已经放入到三级缓存中,此时进行populateBean(A)的时候,因为A中注入了B,则会重新走一遍A所经过的流程,直到populateBean(B),此时B中也注入了A,但是A已经存在于三级缓存中。此时B初始化完成。然后A也会初始化完成

springboot关闭循环依赖检查 spring如何解决循环依赖的_二级缓存_02


总结

1.所有单例的bean在创建完成之前都会提早曝光。

2.提早曝光就是预先为这个bean创建好一个ObjectFactory。一旦发现是循环依赖。就调用ObjectFactory.getObject返回实例。

3.Spring判断循环依赖的条件是earlySingletonObjects里是否存在这个ObjectFactory.getObject生成的object。(或者另外一个map,三级缓存跟二级缓存map是互斥的)