您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關如何用spring源碼剖析spring bean循環(huán)依賴,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
spring bean循環(huán)依賴應該是spring 源碼中比較難的一塊知識了,下面我們結(jié)合代碼還有時序圖,來進行分析,看下spring 是如何優(yōu)雅的處理spring bean的循環(huán)依賴的。
我們都知道spring的IOC 和DI,它可以幫助我們創(chuàng)建對象,并且可以幫我們自動注入需要spring管理的對象。然后會存在一種這樣的情況,在對象 A 中需要依賴 B,而在對象 B 中有又依賴 A,當然可以有更多的對象依賴,他們之間會組成了一個閉環(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é)合代碼來做進一步的分析
創(chuàng)建類BeanA,然后通過set注入BeanB
public class BeanA { private BeanB beanB; public void setBeanB(BeanB beanB) { this.beanB = beanB; } }
創(chuàng)建類BeanB,然后通過set注入BeanA
public class BeanB { private BeanA beanA; public void setBeanA(BeanA beanA) { this.beanA = beanA; } }
將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); } }
在分析源碼前我們先來看下spring 循環(huán)依賴的時序圖,對于后面我們分析源碼會有很大的幫助 原始文件可以從這里下載
我們調(diào)用了ClassPathXmlApplicationContext的帶參構造方法,最終調(diào)用了下面的構造。這里完成了三件事:
super(parent)
初始化父類
setConfigLocations(configLocations)
設置本地的配置信息
refresh()
完成spring容器的初始化
這里我們重點觀察第三個方法,因為spring bean的初始化是在這里完成的
public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { // 初始化父類 super(parent); // 設置本地的配置信息 setConfigLocations(configLocations); // 完成Spring容器的初始化 if (refresh) { refresh(); } }
上面說了,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(); }
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(); } } } }
為了方便閱讀刪除了部分源碼
上面方法中調(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è)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。