透過迴圈引用問題來分析Spring原始碼
本文主要分析Spring的迴圈依賴問題。開始正文之前,我們需要定義這2個類。LoopReferenceA中引用LoopReferenceB,LoopReferenceB中引用LoopReferenceA。
/** * @author cmazxiaoma * @version V1.0 * @Description: TODO * @date 2018/8/14 15:42 */@Componentpublic class LoopReferenceA { @Autowired private LoopReferenceB loopReferenceB;// public LoopReferenceA(@Autowired LoopReferenceB loopReferenceB) {// this.loopReferenceB = loopReferenceB;// }}
/** * @author cmazxiaoma * @version V1.0 * @Description: TODO * @date 2018/8/14 15:43 */@Componentpublic class LoopReferenceB { @Autowired private LoopReferenceA loopReferenceA; }
分析
AbstractApplicationContext中的refresh()方法是Spring中很核心的方法。Spring中所有的方法初始化都是在這個方法中完成的,用於容器初始化。refresh()裡面的細節我就不多說了,本文篇幅有限。只是大致過一下概念,有利於我們後面追蹤分析問題。
prepareRefresh():容器預先準備,記錄容器啟動時間和進行標記狀態
obtainFreshBeanFactory():建立BeanFactory。如果已經存在該工廠,那麼銷燬其裡面的beans和自行關閉。如果沒有,則建立工廠,並且進行裝載BeanDefinition
prepareBeanFactory(): 配置BeanFactory的上下文,比如classLoader和BeanPostProcessor,註冊一些需要解決的依賴和需要忽略的依賴、註冊涉及容器系統環境的bean等等。
postProcessBeanFactory():模板方法,在BeanDefinition被裝載後(所有BeanDefinition被載入,但是沒有bean被例項化),提供一個修改beanFactory容器的入口。
invokeBeanFactoryPostProcessor():在Bean未開始例項之前,提供BeanDefinition修改或者註冊的入口。我們熟悉的PropertyPlaceHolderConfigurer就是在這裡呼叫的。
registerBeanPostProcessor():用於攔截Bean建立的BeanPostProcessor。
initMessageSource():初始化容器所需要的MessageSource,用於國際化處理
initApplicationEventMulticaster():初始化容器的事件廣播器。
onRefresh():模板方法
registerListeners():註冊監聽器
finishBeanFactoryInitialization():完成容器的初始化,裡面會呼叫preInstantiateSingletons()完成單例物件的建立。
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1.Prepare this context for refreshing. prepareRefresh(); // 2.Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3.Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // 4.Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // 5.Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // 6.Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // 7.Initialize message source for this context. initMessageSource(); // 8.Initialize event multicaster for this context. initApplicationEventMulticaster(); // 9.Initialize other special beans in specific context subclasses. onRefresh(); //10. Check for listener beans and register them. registerListeners(); // 11.Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); //12. Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
我們只用關注beanFactory.preInstantiateSingletons()方法。這個方法用於建立單例且是急載入的物件。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. 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)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return 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. beanFactory.preInstantiateSingletons();
這個方法是位於DefaultListableBeanFactory中,這個類實現BeanDefinitionRegistry介面,透過重寫的registerBeanDefinition註冊容器中的BeanDefinition。遍歷所有的BeanDefinitionNames,從mergedBeanDefinitions中獲取RootBeanDefinition。如果bean不是抽象且是單例且是急載入的話,那麼進行下一步判斷。如果bean是FactoryBean型別,還需要判斷裡面的object是否急載入。如果是的話,直接呼叫getBean(beanName)完成建立。如果bean不是FactoryBean型別的話,直接呼叫getBean(beanName)。
@Override public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } } // Trigger post-initialization callback for all applicable beans... for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; } }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
AbstractBeanFactory中的getBean(name),看來是實現了BeanFactory介面的getBean(name)方法,裡面呼叫的是AbstractAutowireCapableBeanFactory中的doGetBean()方法。
//--------------------------------------------------------------------- // Implementation of BeanFactory interface //--------------------------------------------------------------------- @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }
別被這麼長的程式碼嚇到了,其實核心的程式碼也就那麼多。分析原始碼的時候,不要被邊邊角角的程式碼帶偏,我們只用抓住核心程式碼,捲起衣袖開幹即可。何況網上有很多大神寫的關於框架原始碼分析的部落格,站在巨人上面的我們事半功倍。
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); getBean(dep); } } // Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && bean != null && !requiredType.isInstance(bean)) { try { return getTypeConverter().convertIfNecessary(bean, requiredType); } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
抓住Object sharedInstance = getSingleton(beanName);
這行程式碼,我們追蹤這行程式碼,會發現最終呼叫的是DefaultSingletonBeanRegistry中的getSingleton()方法。這裡指的注意是allowEarlyReference的值是true,說明允許提前暴露bean的引用。getSingleton()的作用是檢查快取中或者是例項工廠中是否有對應的bean,這主要是解決了Spring迴圈依賴的問題。因為在建立單例LoopReferenceA物件的時候,會存在依賴LoopReferenceB物件。而建立LoopReferenceB的時候避免出現迴圈依賴的問題,Spring會在LoopReferenceA物件還沒有建立完成之前將建立LoopReferenceA物件的ObjectFactory暴露出來,並且加入到singletonFactories快取中。當LoopReferenceB物件建立的時候,直接從singletonFactories快取中拿到LoopReferenceA的ObjectFactory,再從ObjectFactory獲取LoopReferenceA的例項即可。其實這個時候已經真相大白了,但是我們還需要繼續追蹤,好對Spring這個流程有個更加清晰的概念。
@Override public Object getSingleton(String beanName) { return getSingleton(beanName, true); }
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
如果名字為name的bean例項只是普通的例項,那麼直接返回beanInstance。如果是FactoryBean型別的,獲取其的object物件,將其返回即可。
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory. if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null; if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { // Return bean instance from factory. FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
如果獲取sharedInstance(提前暴露的物件)為null的話,會執行到這裡。Spring只會處理單例情況下出現的迴圈依賴的問題。而對於採取ProtoType策略建立的bean,則不會去解決。如果LoopRefereceA和LoopReferenceB都指定@Scope("prototype"),那這裡肯定會丟擲如圖所示的錯誤。
作者:cmazxiaoma
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4729/viewspace-2816326/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring 迴圈引用(三)原始碼深入分析版Spring原始碼
- require()迴圈引用問題UI
- Spring原始碼分析:Spring的迴圈依賴分析Spring原始碼
- Spring IOC原始碼深度剖析:Spring IoC迴圈依賴問題Spring原始碼
- spring原始碼之bean的初始化及迴圈引用Spring原始碼Bean
- Spring原始碼分析之IOC迴圈依賴Spring原始碼
- 3.2spring原始碼系列----迴圈依賴原始碼分析Spring原始碼
- FastJson中迴圈引用的問題ASTJSON
- Spring原始碼分析(三)手寫簡單的IOC容器和解決迴圈依賴問題Spring原始碼
- Spring如何解決迴圈引用Spring
- JavaScript 深複製的迴圈引用問題JavaScript
- Spring原始碼分析之迴圈依賴及解決方案Spring原始碼
- Spring原始碼--debug分析迴圈依賴--構造器注入Spring原始碼
- 怎麼解決引用計數 GC 的迴圈引用問題?GC
- Spring5.0原始碼學習系列之淺談迴圈依賴問題Spring原始碼
- 迴圈引用
- 【FastJSON】解決FastJson中“$ref 迴圈引用”的問題ASTJSON
- 面試題:Spring 的迴圈依賴問題面試題Spring
- 原始碼分析:CyclicBarrier 之迴圈柵欄原始碼
- 迴圈請求報204問題分析
- Python迴圈引用是什麼?如何避免迴圈引用?Python
- ARC下的block導致的迴圈引用問題解析BloC
- iOS迴圈引用iOS
- Spring Ioc原始碼分析系列--自動注入迴圈依賴的處理Spring原始碼
- 3.1 spring5原始碼系列--迴圈依賴 之 手寫程式碼模擬spring迴圈依賴Spring原始碼
- 【spring原始碼系列】之【Bean的迴圈依賴】Spring原始碼Bean
- 解決迴圈引用
- 從原始碼層面深度剖析Spring迴圈依賴原始碼Spring
- spring原始碼閱讀筆記09:迴圈依賴Spring原始碼筆記
- Spring——為什麼會有迴圈依賴(原始碼)Spring原始碼
- 如何解決使用JSON.stringify時遇到的迴圈引用問題JSON
- Spring基礎系列-Spring事務不生效的問題與迴圈依賴問題Spring
- Spring的3級快取和迴圈引用的理解Spring快取
- Spring 的迴圈依賴,原始碼詳細分析 → 真的非要三級快取嗎Spring原始碼快取
- Spring啟動過程——原始碼分析Spring原始碼
- springboot bean的迴圈依賴實現 原始碼分析Spring BootBean原始碼
- Spring的迴圈依賴,學就完事了【附原始碼】Spring原始碼
- spring原始碼深度解析— IOC 之 迴圈依賴處理Spring原始碼