# Spring 循环依赖 🎉

Spring中的循环依赖包括: 构造器循环依赖、setter循环依赖。

# 构造器的依赖

Spring对于构造器的依赖、无法解决。只会抛出BeanCurrentlyInCreationException异常。

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

/**
 * Callback before singleton creation.
 * <p>The default implementation register the singleton as currently in creation.
 * @param beanName the name of the singleton about to be created
 * @see #isSingletonCurrentlyInCreation
 */
protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

因为无法创建出这么一个不完整的bean在一个构造函数依赖的关系中,所以Spring不能解决构造器循环依赖。

官方文档说明如下:

Circular dependencies

If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).

# setter 循环依赖

Spring是如何解决的?其实很简单,在Spring获取单例中有一个三级缓存,代码如下:

// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

/** 一级缓存,保存singletonBean实例: bean name --> bean instance */
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 三级缓存,保存singletonBean生产工厂: bean name --> ObjectFactory */
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 二级缓存,保存早期未完全创建的Singleton实例: bean name --> bean instance */
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// ...

/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

获取单例Bean步骤分析

  1. 先从一级缓存singletonObjects中去获取,如果获取到就直接return(我们知道在Spring中,所有单例的bean初始化完成后都会存放在一个ConcurrentHashMapsingletonObjects,一级缓存)中,beanNamekey,单例beanvalue);
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取,如果获取到就直接return
  3. 如果获取不到并且允许singletonFactoriesallowEarlyReference=true)通过getObject()获取,那么就从三级缓存singletonFactory.getObject()获取; 如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects

    加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

那么当AB类的互相依赖注入时,初始化流程图(借用大佬的图,学习使用,如有侵权,请联系作者删除)如下: 循环依赖初始化流程图 整个流程步骤总结如下:

  1. 实例化单例beanA,并将它的创建工厂放入三级缓存(singletonFactories),强调说明:加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决
  2. 填充beanA时,发现依赖beanB,那么此时需要去容器内获取单例beanB
  3. 当在容器内没有获取到beanB,则开始创建单例beanB
  4. 同操作1,实例化单例beanB,并将它的创建工厂放入三级缓存(singletonFactories);
  5. 同操作2,填充beanB时,发现依赖beanA,那么此时需要去容器内获取单例beanA
  6. 因为在三级缓存中存在beanA,那么就从三级缓存singletonFactory.getObject()获取beanA,获取成功以后,就从singletonFactories中移除,并且放进二级缓存earlySingletonObjects中;
  7. beanB获取到了一个不完整的beanA,已经成功持有beanA引用,所以beanB初始化成功,并且把beanB放入到singletonObjects一级缓存中;
  8. 继续初始化beanA,依赖的beanB存在于一级缓存,直接可以获取到,所以beanA也可以初始化成功。

# 为啥是三级缓存,二级缓存是否可以

想要弄清楚这个问题,我们先了解一下循环依赖对AOP代理对象创建流程和结果的影响

@Service
public class HelloServiceImpl implements HelloService {

    @Autowired
    private HelloService helloService;
    
    @Transactional
    @Override
    public Object hello(Integer id) {
        return "service hello";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

Service类使用到了事务,所以最终会生成一个JDK动态代理对象。刚好它又存在自己引用自己的循环依赖。那么这个Bean的创建概要描述如下:

查看AbstractAutowireCapableBeanFactory.createBean的源码。


参考文档

Last Updated: 2 years ago