溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何用spring源碼剖析spring bean循環(huán)依賴

發(fā)布時間:2021-12-28 15:04:25 來源:億速云 閱讀:155 作者:柒染 欄目:大數(shù)據(jù)

本篇文章給大家分享的是有關如何用spring源碼剖析spring bean循環(huán)依賴,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

spring bean循環(huán)依賴

spring bean循環(huán)依賴應該是spring 源碼中比較難的一塊知識了,下面我們結(jié)合代碼還有時序圖,來進行分析,看下spring 是如何優(yōu)雅的處理spring bean的循環(huán)依賴的。

什么是bean的循環(huán)依賴

我們都知道spring的IOC 和DI,它可以幫助我們創(chuàng)建對象,并且可以幫我們自動注入需要spring管理的對象。然后會存在一種這樣的情況,在對象 A 中需要依賴 B,而在對象 B 中有又依賴 A,當然可以有更多的對象依賴,他們之間會組成了一個閉環(huán),如下圖 如何用spring源碼剖析spring bean循環(huán)依賴 在spring 初始化bean A的時候發(fā)現(xiàn)bean A依賴于bean B,然后去實例化bean B,實例化bean B的時候又發(fā)現(xiàn)bean B依賴于bean A...,這樣陷入了一個無限循環(huán)的過程中,最終可能會導致內(nèi)存溢出。當然,這只是我們的一個設想,spring當然不會讓這樣的事情發(fā)生。spring提供一個很優(yōu)雅的方式來幫我們解決了bean之間循環(huán)依賴的問題,通過引入三級緩存機制來解決。 另外再說一點,不是所有的循環(huán)依賴spring 都可以解決,以下兩種方式spring也是無法解決的

  • 依賴是通過構造方法實現(xiàn)的

  • scope 為prototype,也就是原型的情況

在正式開始之前,我們再說下spring的三級緩存以及定義 先來看下代碼中是如何定義的

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

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

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  • singletonObjects 一級緩存, 用于存放完全初始化之后的bean,也就是說,所有的準備工作已經(jīng)完成了,可以拿出來使用了

  • earlySingletonObjects 二級緩存,存放原始的bean對象,這時候bean只是實例化完成,還沒有進行屬性設置等工作

  • singletonFactories 三級緩存,存放只是實例化,還沒有進行其它任何操作的bean對象

下面我們結(jié)合代碼來做進一步的分析

依賴代碼

bean A

創(chuàng)建類BeanA,然后通過set注入BeanB

public class BeanA {

	private BeanB beanB;

	public void setBeanB(BeanB beanB) {
		this.beanB = beanB;
	}
}

bean B

創(chuàng)建類BeanB,然后通過set注入BeanA

public class BeanB {

	private BeanA beanA;

	public void setBeanA(BeanA beanA) {
		this.beanA = beanA;
	}
}

配置文件applicationContext.xml

將BeanA 和BeanB 交給spring來進行管理

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

	<!--循環(huán)依賴問題-->
	<bean id="beanA" class="com.lagou.test.BeanA">
		<property name="beanB" ref="beanB"/>
	</bean>
	<bean id="beanB" class="com.lagou.test.BeanB">
		<property name="beanA" ref="beanA"/>
	</bean>
</beans>

測試代碼

使用ClassPathXmlApplicationContext來初始化bean

public class BeanTest {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
		Object beanA = applicationContext.getBean("beanA");
		System.out.println(beanA);
	}
}

源碼分析

循環(huán)依賴時序圖

在分析源碼前我們先來看下spring 循環(huán)依賴的時序圖,對于后面我們分析源碼會有很大的幫助 原始文件可以從這里下載

如何用spring源碼剖析spring bean循環(huán)依賴

ClassPathXmlApplicationContext.java

我們調(diào)用了ClassPathXmlApplicationContext的帶參構造方法,最終調(diào)用了下面的構造。這里完成了三件事:

  1. super(parent)初始化父類

  2. setConfigLocations(configLocations)設置本地的配置信息

  3. refresh()完成spring容器的初始化

這里我們重點觀察第三個方法,因為spring bean的初始化是在這里完成的

	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		// 初始化父類
		super(parent);
		// 設置本地的配置信息
		setConfigLocations(configLocations);
		// 完成Spring容器的初始化
		if (refresh) {
			refresh();
		}
	}

AbstractApplicationContext.java

上面說了,refresh方法是初始化spring容器的,所以這是一個核心方法。這里面包含beanFactory 和bean的初始化操作,因為方法比較多,對每個方法都進行了注釋,不一一贅述了,這里我們重點關注finishBeanFactoryInitialization(),這個方法做了一下操作

  • 初始化所有剩下的非懶加載的單例bean

  • 初始化創(chuàng)建非懶加載方式的單例Bean實例(未設置屬性)

  • 填充屬性

  • 初始化方法調(diào)用(比如調(diào)用afterPropertiesSet方法、init-method方法)

  • 調(diào)用BeanPostProcessor(后置處理器)對實例bean進行后置處理 所以我們接下來看下這個方法

public void refresh() throws BeansException, IllegalStateException {
		// 對象鎖加鎖
		synchronized (this.startupShutdownMonitor) {
			//刷新前的預處理,表示在真正做refresh操作之前需要準備做的事情:
			prepareRefresh();
			/*
			 	獲取BeanFactory;默認實現(xiàn)是DefaultListableBeanFactory
                加載BeanDefition 并注冊到 BeanDefitionRegistry
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			//BeanFactory的預準備工作(BeanFactory進行一些設置,比如context的類加載器等)
			prepareBeanFactory(beanFactory);
			try {
				//BeanFactory準備工作完成后進行的后置處理工作
				postProcessBeanFactory(beanFactory);
				//實例化實現(xiàn)了BeanFactoryPostProcessor接口的Bean,并調(diào)用接口方法
				invokeBeanFactoryPostProcessors(beanFactory);
				//注冊BeanPostProcessor(Bean的后置處理器),在創(chuàng)建bean的前后等執(zhí)行
				registerBeanPostProcessors(beanFactory);
				//初始化MessageSource組件(做國際化功能;消息綁定,消息解析);
				initMessageSource();
				//初始化事件派發(fā)器
				initApplicationEventMulticaster();
				//子類重寫這個方法,在容器刷新的時候可以自定義邏輯;如創(chuàng)建Tomcat,Jetty等WEB服務器
				onRefresh();
				//注冊應用的監(jiān)聽器。就是注冊實現(xiàn)了ApplicationListener接口的監(jiān)聽器bean
				registerListeners();

				/*  Instantiate all remaining (non-lazy-init) singletons.
					初始化所有剩下的非懶加載的單例bean
					初始化創(chuàng)建非懶加載方式的單例Bean實例(未設置屬性)
                    填充屬性
                    初始化方法調(diào)用(比如調(diào)用afterPropertiesSet方法、init-method方法)
                    調(diào)用BeanPostProcessor(后置處理器)對實例bean進行后置處理
				 */
				finishBeanFactoryInitialization(beanFactory);

				//完成context的刷新。主要是調(diào)用LifecycleProcessor的onRefresh()方法,并且發(fā)布事件(ContextRefreshedEvent)
				finishRefresh();
			} catch (BeansException ex) {
				destroyBeans();
				cancelRefresh(ex);
				throw ex;
			} finally {
				resetCommonCaches();
			}
		}
	}

這個方法的最后調(diào)用了beanFactory的preInstantiateSingletons()方法,接下繼續(xù)跟蹤preInstantiateSingletons()方法

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

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}
		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);
		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();
		// Instantiate all remaining (non-lazy-init) singletons.
		// 實例化所有立即加載的單例bean
		beanFactory.preInstantiateSingletons();
	}

DefaultListableBeanFactory.java

preInstantiateSingletons()中獲取了所有需要spring管理的bean的name,然后需要遍歷,進行bean的創(chuàng)建,核心方法是getBean(beanName),根據(jù)獲取到的bean name進行bean的初始化,所以接下來我們跟蹤getBean(beanName)

public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}
		// 獲取所有bean的名字
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
		// 觸發(fā)所有非延遲加載單例bean的初始化,主要步驟為getBean
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					// 如果是FactoryBean則加&
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						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 {
					// 實例化當前bean
					getBean(beanName);
				}
			}
		}
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

開始處理循環(huán)依賴

為了方便閱讀刪除了部分源碼

上面方法中調(diào)用了getBean(),getBean()又調(diào)用了doGetBean(),經(jīng)過前面的鋪墊,其實到這里,才開始了真正的循環(huán)依賴相關的源碼邏輯。 之前我們說過spring 的三級緩存,這里根據(jù)bean name先調(diào)用getSingleton(beanName)方法,getSingleton(beanName)方法中又調(diào)用了它的重載方法。我們先假設要去找的bean為beanA,spring先從一級緩存中去找我們要的beanA,找不到的話去二級緩存中去找,二級緩存還找不到去一級緩存中去拿,當然,因為剛加載,所以三級緩存中也是沒有的。所以代碼繼續(xù)往下執(zhí)行。

	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// 解析beanName 如果以&開頭去掉&開頭,如果是別名獲取到真正的名字
		final String beanName = transformedBeanName(name);
		// 嘗試從一二三級緩存中獲取 bean
		Object sharedInstance = getSingleton(beanName);
		// 如果已經(jīng)存在則返回
		if (sharedInstance != null && args == null) {
			// 針對 FactoryBean 的處理
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		} else {
			// 如果是prototype類型且開啟允許循環(huán)依賴,則拋出異常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName)
				// 創(chuàng)建單例bean
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							// 創(chuàng)建 bean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
			}
		}
		return (T) bean;
	}


	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
	
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//先從一級緩存singletonObjects中拿
		Object singletonObject = this.singletonObjects.get(beanName);
		// isSingletonCurrentlyInCreation(beanName)判斷當前單例bean是否正在創(chuàng)建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//一級緩存沒有從二級緩存earlySingletonObjects中去拿
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					//二級緩存沒有從三級緩存singletonFactories去拿
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						//三級緩存中有的話放入二級緩存中,同時從一級緩存中刪除
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}	

接著上面的doGetBean()方法往下走,又調(diào)用了getSingleton()的另一個重載方法,可以看到第二個參數(shù)是一個ObjectFactory接口,而這里spring采用了一個lambda表達式來實現(xiàn)了getObject()方法。調(diào)用傳進來的lambda表達式singletonFactory.getObject(),進行bean的創(chuàng)建。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				// 是否正在銷毀,異常
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				// 驗證完要真正開始創(chuàng)建對象,先標識該bean正在被創(chuàng)建,因為spingbean創(chuàng)建過程復雜,步驟很多,需要標識
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 傳進來的調(diào)用,lamda表達式使用
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

接下來看下lambda表達式中的createBean(beanName, mbd, args)方法是如何實現(xiàn)的 createBean()中其實又調(diào)用了doCreateBean(),它才是真正干活的。這里首先調(diào)用對createBeanInstance(beanName, mbd, args)beanA進行實例化,然后放入三級緩存中,接下來調(diào)用populateBean(beanName, mbd, instanceWrapper)beanA進行屬性填充,在我們這里其實就是填充beanB,我們繼續(xù)看填充方法中做了什么

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
		// 拿到Bd
		RootBeanDefinition mbdToUse = mbd;
		//....
		try {
			// 進入,真真正正創(chuàng)建bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			throw ex;
		}
	}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			// 創(chuàng)建 Bean 實例,僅僅調(diào)用構造方法,但是尚未設置屬性
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
			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));
		}
		//...
		Object exposedObject = bean;
		try {
			// Bean屬性填充
			populateBean(beanName, mbd, instanceWrapper);
			// 調(diào)用初始化方法,應用BeanPostProcessor后置處理器
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		//...
		return exposedObject;
	}

populateBean()中最后調(diào)用了applyPropertyValues(beanName, mbd, bw, pvs),applyPropertyValues(beanName, mbd, bw, pvs)方法中有調(diào)用了resolveValueIfNecessary(pv, originalValue)resolveValueIfNecessary(pv, originalValue)中又調(diào)用了resolveReference(argName, ref),好了終于到重要關頭了,在resolveReference(argName, ref)中我們可用看到,又調(diào)用了getBean(refName)方法,獲取beanB。呀,這是什么情況,怎么又回到起點了?哈哈,別著急,我們繼續(xù)分析。 既然調(diào)用了getBean()方法,我們不妨在回頭看看(代碼我就不再貼一遍了)。 getBean()繼續(xù)調(diào)用doGetBean()方法,然后繼續(xù)調(diào)用getSingleton()方法去緩存中拿beanB,然后發(fā)現(xiàn)beanB也不再緩存中,然后就開始去創(chuàng)建beanB。創(chuàng)建beanB先進行實例化,然后放入到緩存中,之后再調(diào)用populateBean()進行屬性填充(其實就是填充依賴beanA),之前我們提到beanA,在實例化beanB之前已經(jīng)實例化完成并放入到三級緩存中了,這里我們就可以使用了,雖然還是個娃娃,但是夠用了。到這里beanB屬性填充之后就可以長大,成為完成的bean了。這時候?qū)?strong>beanB放入到緩存池中,我們就可以回過頭來填充beanA的屬性了。到這里 ,beanA、beanB就完成了他們整個bean的創(chuàng)建過程了。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	//...
	if (pvs != null) {
		applyPropertyValues(beanName, mbd, bw, pvs);
	}
}

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		//...
		for (PropertyValue pv : original) {
			if (pv.isConverted()) {
				deepCopy.add(pv);
			} else {
				String propertyName = pv.getName();
				Object originalValue = pv.getValue();
				//
				Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
				Object convertedValue = resolvedValue;
				//...
			}
		}
		//...
	}

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
		if (value instanceof RuntimeBeanReference) {
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			return resolveReference(argName, ref);
		}
		//...
	}

	private Object resolveReference(Object argName, RuntimeBeanReference ref) {
		try {
			Object bean;
			String refName = ref.getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			if (ref.isToParent()) {
				bean = this.beanFactory.getParentBeanFactory().getBean(refName);
			}
			else {
				bean = this.beanFactory.getBean(refName);
				this.beanFactory.registerDependentBean(refName, this.beanName);
			}
			if (bean instanceof NullBean) {
				bean = null;
			}
			return bean;
		}
		catch (BeansException ex) {}	

以上就是如何用spring源碼剖析spring bean循環(huán)依賴,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI