美文网首页
Spring源码解读之@Autowired如何解决循环依赖

Spring源码解读之@Autowired如何解决循环依赖

作者: 穹柏 | 来源:发表于2020-10-27 10:56 被阅读0次

    @[TOC]

    @Autowired的what&how

    在spring框架下,我们可以通过@Autowired注解对属性或者方法参数进行标注,当spring ioc容器初始化时,会帮我们从容器中拿到对应的实例进行注入

    什么是循环依赖

    假如现在有两个Bean如下所示

    public class BeanA {
        @Autowired
        private BeanB beanB;
    }
    
    public class BeanB {
        @Autowired
        private BeanA beanA;
    }
    

    然后我们通过annotationConfigApplicationContext#register将两个bean的信息注入到容器中,最后通过refresh进行容器到初始化操作

    public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
            annotationConfigApplicationContext.register(Bean1.class);
            annotationConfigApplicationContext.register(Bean2.class);
            annotationConfigApplicationContext.refresh();
        }
    

    可以看到A跟B互相依赖,试着想象:当容器先初始化beanA时,必然要对属性beanB进行赋值,这个时候容器中还没有beanB,那么势必会触发beanB的初始化流程,而beanB初始化的完成也需要对属性beanA赋值,但beanA还未初始化完成,这里就产生了所谓的循环依赖。

    spring如何解决循环依赖

    这里有一个很关键的属性:

    public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
        /** Cache of singleton factories: bean name to ObjectFactory. */
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    }
    

    key是beanName,value是一个对象工厂,我们点进去看一下

    public interface ObjectFactory<T> {
    
        T getObject() throws BeansException;
    
    }
    

    其实这里的getObject()就是最终解决循环依赖所调用的方法。
    那么程序是怎样执行到这的呢?
    我们先从bean的创建入手
    如果容器还未实例化bean,那么就会走到这里

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
                throws BeanCreationException {
            BeanWrapper instanceWrapper = null;
            if (instanceWrapper == null) {
                //实例化bean,如果@Autowired加在构造方法上,
                //那么就会在这里完成注入
                //因为下面的回调还未注册,所以这里无法解决循环依赖
                instanceWrapper = createBeanInstance(beanName, mbd, args);
            }
            
            final Object bean = instanceWrapper.getWrappedInstance();
            
            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");
                }
                //往单例工厂(之前说的singletonFactories)中添加一个
                //ObjectFactory的匿名实现作为回调,
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
                
                //属性赋值,处理@Autowired(非构造方法)
                populateBean(beanName, mbd, instanceWrapper);
            }
    

    这里我们发现,在实例化bean跟对属性赋值之间有一个addSingletonFactory的操作,作用是注册一个可以获取当前正在创建的bean的一个回调

        protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            synchronized (this.singletonObjects) {
                if (!this.singletonObjects.containsKey(beanName)) {
                    this.singletonFactories.put(beanName, singletonFactory);
                }
            }
        }
    

    进入回调,发现回调默认返回的就是bean本身

        protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
            Object exposedObject = bean;
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                        exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    }
                }
            }
            return exposedObject;
        }
        
        default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
            //  返回bean本身
            return bean;
        }
    

    ok,这里得出一个结论,即使bean未初始化完成,spring也提供了方法来获取这个bean的实例。
    如果应用到我们上面的栗子中来就是:

    1. beanA实例化完成
    2. 添加获取beanA的回调到singletonFactories
    3. 调用populateBean,处理@Autowired,注入beanB

    因为beanB还未创建,那么势必会进入创建beanB的流程,当beanB也走到populateBean时,也需要完成beanA的注入,这时就会尝试从beanFactory中获取beanA,这里最终会进到
    AbstractBeanFactory的doGetBean中

        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;
    
            // Eagerly check singleton cache for manually registered singletons.
            Object sharedInstance = getSingleton(beanName);
        }
    

    这里很关键,我们进入getSingleton(beanName)

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

    当beanB走到这里时通过beanA的beanName获取beanA,首先会尝试从singletonObjects中获取,这里肯定获取不到,因为singletonObjects的put操作是在bean初始化完成之后。所以只能通过调用之前注册的回调singletonFactory.getObject()来获取beanA。
    那么到此beanA注入到beanB的顺利完成,当beanB初始化完成之后,其实beanA的getBean()也就返回了beanB的引用,到此beanA也可以顺利完成依赖注入。

    相关文章

      网友评论

          本文标题:Spring源码解读之@Autowired如何解决循环依赖

          本文链接:https://www.haomeiwen.com/subject/xygtvktx.html