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注解实现构造器的循环依赖

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容