透過迴圈引用問題來分析Spring原始碼

youou發表於2021-09-09

本文主要分析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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章