spring循环依赖

引言

假如有class A;class B。其中A依赖B,B依赖A。那么在创建对象时就会有循环依赖的问题,Spring是如何解决这个问题的呢?如果这个依赖是在构造器中,spring还可以解决循环依赖的问题吗?

@Component
public class A {

    @Autowired
    private B b;
}
@Component
public class B {
    @Autowired
    private A a;
}

一、单例field属性的循环依赖

这一节比较长,但很重要。​
首先,我们需要单步调试代码,帮助我们更方便的了解spring的方法调用关系。这一步可参考下述代码:

public class TestMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aac = new AnnotationConfigApplicationContext(TestMain.class);
        aac.getBean("a");
    }
}

​我们通过单步调试,可以跟踪到获取bean的核心方法:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 1 singletonObjects,存储成熟的完整bean对象
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 2 earlySingletonObjects 存储早期对象,早期对象初始化不完全
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    // 3 singletonFactories 存储bean工厂,解决有后置处理器的bean,找到其最终对象的factory
                    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);
    }

​ 上述代码中有三个缓存map,分别存储不同时期的对象。

  • singletonObjects存储完整的bean,bean完全创建完成,将其放入singletonObjects。获取bean也先由这里获取。
  • earlySingletonObjects存储早期对象,该早期对象中存在不可用的属性值。
  • singletonFactories存储bean工厂对象,是经过AOP增强的最终工厂对象。

​ 上述代码中,我们看到有三级缓存,这是解决循环依赖的关键。简单看下getSingleton做了以下几件事:查询一级缓存singletonObjects,然后查询二级缓存earlySingletonObjects,最后查询三级缓存singletonFactory,同时将bean放到二级缓存,并由三级缓存清除。
但是这里对一个全新的bean,返回还是null,而且各级缓存中的对象是什么时候放进去的呢?我们先来看下\color{red}{doGetBean}:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
......
    Object sharedInstance = getSingleton(beanName);
......
                // 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);
                }
......

上述分析的getSingleton是第5行,这里我们先关注第9行的getSingleton方法。单例模式下,通过getSingleton(为和第5行的getSingleton区分,我们称这个为第二个getSingleton)获取bean,实现如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                ......
                    beforeSingletonCreation(beanName);
                ......
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                ......
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
        }
                ......
    }

​ 上面有singletonFactory.getObject(),该方法的实现是在入参中定义的,即createBean(beanName, mbd, args)(后面详细分析)。此外还有一个addSingleton的方法,实现如下:

protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

​ 到这里,我们可以看到,一个全新的对象,是通过createBean创建完成,然后放到singletonObjects的。

​ 接下来我们分析\color{red}{createBean}:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {
......
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
......

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            ......
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }
        ......
        return exposedObject;
    }

​ 在创建bean的时候,先调用createBeanInstance生成一个BeanWrapper。他的最终调用只是newInstance了一个对象,没有对对象进行初始化,这就为后续的aop增强,以及循环对象的注入提供了条件。

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        try {
            ReflectionUtils.makeAccessible(ctor);
            return ctor.newInstance(args);
        }
        ......
    }

​ 继续回到doCreateBean方法,如下所示:

if (earlySingletonExposure) {
            ......
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }

​ 当bean处于创建中,并且存在循环依赖时,做了addSingletonFactory操作,如下所示:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

​ 这里我们可以看到,singletonFactory被放到了singletonFactories,singletonFactory是new的那个ObjectFactory。这里ObjectFactory为什么要这么定义,还有为什么需要使用singletonFactories?二级缓存不能解决循环依赖问题?这就需要关注getEarlyBeanReference。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    if (exposedObject == null) {
                        return null;
                    }
                }
            }
        }
        return exposedObject;
    }

​ getEarlyBeanReference是由三级缓存singletonFactory中获取对象的实现方法,它调用的是org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference方法。实现如下:

public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            this.earlyProxyReferences.add(cacheKey);
        }
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

继续看下wrapIfNecessary,org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        ......

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

​ 上面通过createProxy方法,创建了带有aop增强设置的方法。到此,第三级缓存singletonFactory的作用我们就清楚了,主要是解决aop增强的实现。到这里我们可以得知new的ObjectFactory,最终帮助我们拿到了aop增强后的对象。

​ 接下来需要注意的方法是populateBean:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean:

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        PropertyValues pvs = mbd.getPropertyValues();
......
        if (hasInstAwareBpps || needsDepCheck) {
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }

        applyPropertyValues(beanName, mbd, bw, pvs);
    }

​ populateBean用于填充对象的属性。这里会填充对象依赖的其他对象信息。上述代码的postProcessPropertyValues方法,最终会调用到:

org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates。

protected Map<String, Object> findAutowireCandidates(
            String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
......
        for (String candidateName : candidateNames) {
            if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
                result.put(candidateName, getBean(candidateName));
            }
        }
......
    }

​ 上面的candidateName表示依赖的对象名称(B),这里可以看到,对于依赖的对象,又会重新调用getBean获取bean对象。具体调用,可以参考下面的调用堆栈图:
循环依赖注入.png

\color{red}{小结}
​ 我们清楚了三级缓存的作用,下面我们结合上述分析,看看三级缓存是如何解决循环依赖问题的。

​ 假设A依赖B,B依赖A。获取A时,A和B在三级缓存都不存在,因此第一个getSingleton返回null。之后会调doGetBean创建A,创建A时,先调createBeanInstance得到一个newInstance的没有实例化的对象。然后把对象A的工厂类放到singletonFactories,这里需要提的是,若A有做AOP增强,那么放到singletonFactories的就是AOP增强后工厂类。

​ 接下来会在创建A中执行populateBean方法,进一步会调getBean,获取B,这将重复上述过程。

​ 在获取B时,创建B的方法也会走到populateBean,这时,B又会获取A的对象。这时第二次获取A对象的getBean方法,会在执行第一个getSingleton时,检查到第三级缓存singletonFactories中有A的工厂方法,于是将对象A放到早期对象缓存earlySingletonObjects中。这轮查询,有得到一个早期的对象A,B将使用这个早期的对象A,完成对象B的创建。

​ B对象创建完成,A对象还在earlySingletonObjects中,在getSingleton中最后的addSingleton方法,会将对象A由二级缓存放到一级缓存singletonObjects中,这时获取到完整的对象A。

二、构造器中的循环依赖

​ 接下来我们再看看,spring能否处理构造器中包含循环依赖的问题。

@Component
public class A {
    private B cb;

    @Autowired
    A(B b){
        this.cb = b;
    }
}
@Component
public class B {
    private A ca;

    @Autowired
    B(A a){
        this.ca = a;
    }
}

​ 同样,我们还是假设A的构造器中依赖B,B的构造器中依赖A。我们回顾上述第二个getSingleton中的beforeSingletonCreation方法:

protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

​ 这里,singletonsCurrentlyInCreation保存的是当前正在创建,但是还没有创建完毕的类,根据上述对循环依赖的分析,当第二次创建A时,singletonsCurrentlyInCreation中已经包含A。这里,我们看下singletonsCurrentlyInCreation的定义:

private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));

​ 这里,使用的是ConcurrentHashMap来保存,add方法实现为:

public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }

​ 也就是说,当map中已经包含A时,ConcurrentHashMap的put方法会返回旧值(非null),这里的add方法会返回false。通过调试,发现inCreationCheckExclusions也是空,因此,这时会抛BeanCurrentlyInCreationException异常,导致创建A失败。因此,spring无法直接解决构造器中包含循环依赖的对象。

​ 到这里,我们会问到,spring为什么要这么设计呢?其实原因很简单,spring的早期对象A没有被初始化,当newInstatnce一个对象B时,会执行B的构造方法,如果B的构造方法中对早期对象A进行操作,可能会抛异常。

​ 那么。Spring无法解决这个问题吗?不是的,在构造函数的循环依赖参数前使用@Lazy注解,即可在构造器中使用循环依赖。

@Component
public class B {
    private A ca;

    @Autowired
    B(@Lazy A a){
        this.ca = a;
    }
}

三、总结

1.spring通过使用三级缓存来解决循环依赖的问题,之所以使用三级缓存而不是二级缓存,是因为还需要处理aop增强的类。
2.spring无法直接解决构造器循环依赖,但可以使用@Lazy注解实现构造器的循环依赖

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342