什么是循环依赖
@Service
public class AService {
@Autowired
private BService bService;
}
@Service
public class BService {
@Autowired
private AService aService;
}
这其实就是 Spring 环境下典型的循环依赖场景,但并非所有循环依赖,Spring 都可以解决。针对构造器注入的循环依赖以及原型模式的 Bean 的循环依赖(因为每次都是创建一个新对象,无法利用缓存,容器虽然可以启动成功,但真正实例化 Bean 的时候还是会抛出异常),Spring 是无法解决的:
@Service
public class AService {
public AService(BService b) {}
}
@Service
public class BService {
public BService(AService a) {}
}
针对这种情况,Spring 在启动时会抛出 BeanCurrentlyInCreationException 异常。抛出异常的根本原因是 Spring 解决循环依赖依靠的是 Bean 的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态,而构造器正是用来完成实例化的,所以构造器的循环依赖无法解决。
针对构造器注入的循环依赖,可以改为属性或字段注入,或使用 @Lazy 延迟注入。比如如下代码:
@Service
public class AService {
public AService(@Lazy BService service) {}
}
@Service
public class BService {
public BService(@Lazy AService service) {}
}
其实,这种 @Lazy 方式注入的就不是实际的类型了,而是代理类,获取的时候会通过代理去拿值(实例化)。所以它可以解决循环依赖无法实例化的问题。
如何解决循环依赖
在实例化 Bean 的过程中,Spring 采用了三级缓存机制来解决 Spring Bean 的循环依赖问题:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 一级缓存,用于存放完全初始化好的bean,从该缓存中取出的bean可以直接使用
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存,提前曝光的bean,存放原始的bean对象(尚未填充属性),用于解决循环依赖
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存,缓存单例对象工厂,存放bean工厂对象,用于解决循环依赖
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}
Bean 的实例化过程如下图所示:
到目前为止,发现使用二级缓存似乎就能解决循环依赖的问题。但 Spring 还提供了 AOP(面向切面编程)的功能,会动态增强对象,不管使用 JDK 的动态代理和 Cglib 动态代理,都会生成一个全新的对象。下图中我标出了 AOP 动态增强的位置。
此时就会出现一个问题,因为经过 AOP 以后,生成的是增强后的 bean 对象,也就是一个全新的对象,我们可以看到经过图中的流程后,单例池中会存在两个 bean:增强后的 a、b 对象,此时 a 对象中依赖的 b 为增强后的对象,而 b 对象依赖的 a 是为原始对象,未增强的。所以使用二级缓存解决不了循环依赖中发生过 aop 的这类引用问题。
为了解决二级缓存中 AOP 生成新对象的问题,Spring 中的解决方案是:提前 AOP,如果我们能够提前 AOP 就能解决上面的问题了,提前 AOP 指的就是,在加载 B 的流程中,如果发生了循环依赖,就是说 b 又依赖了 a,我们就要对 a 执行 aop,提前获取增强以后的 a 对象,这样 b 对象依赖的 a 对象就是增强以后的 a 了。三级缓存的 key 是 beanName,value 是一个 lambda 表达式,这个 lambda 表达式的作用就是进行提前 AOP。
下面是加入了三级缓存和 AOP 的流程图:
上面就是三级缓存的作用,其中有个三级缓存到二级缓存的升级过程,这个非常重要,主要是防止重复 aop。下面我们顺着源码分析:
1. doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 获取beanName
final String beanName = transformedBeanName(name);
Object bean;
// 首先先尝试获取bean,如果加载过就不会在重复加载了
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
// 返回对应的实例,有时存在诸如FactoryBean的情况并不是直接返回实例本身而是返回指定方法返回的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 只有在单例情况下才会尝试解决循环依赖
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
......
try {
// 根据beanName获取eanDefinition对象
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
......
// 单例模式创建bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建bean实例
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型模式创建bean
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
......
}
}
return (T) bean;
}
在具体实例化 Bean 的过程中,Spring 首先调用 getSingleton 方法尝试从单例缓存池中获取对应实例,因为在创建单例 Bean 的时候会存在依赖注入的情况,而在创建依赖时为了避免循环依赖,Spring 创建 Bean 的原则是不等 Bean 创建完成就会将创建 Bean 的 ObjectFactory 提早曝光,也就是将 ObjectFactory 加入到缓存中,一旦下个 Bean 创建时要依赖上个 Bean 则直接使用缓存的 ObjectFactory。具体代码如下:
2. getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先去一级缓存中获取,如果获取的到说明bean已经存在,直接返回
Object singletonObject = this.singletonObjects.get(beanName);
// 如果一级缓存中不存在,则去判断该bean是否在创建中,如果该bean正在创建中,说明此时发生了循环依赖
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 如果发生循环依赖,首先去二级缓存中获取,如果获取到则返回,这个地方就是获取aop增强以后的bean,因为执行aop后会将其从三级缓存升级到二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果二级缓存中不存在,且允许提前访问三级引用
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 如果三级缓存中的lambda表达式存在,执行aop,获取增强以后的对象,为了防止重复aop,将三级缓存删除,升级到二级缓存中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
先从一级缓存 singletonObjects 中去获取,如果获取到就直接返回。如果获取不到或者对象正在创建中,那就再从二级缓存 earlySingletonObjects 中获取,如果获取到就直接返回。如果还是获取不到,就从三级缓存 singletonFactories 中获取。如果获取到就从 singletonFactories 中移除,并且放进 earlySingletonObjects。其实也就是从三级缓存移动到了二级缓存中。
3. creatBean
如果从缓存中获取不到,则会执行单例 Bean 的实例化过程,核心逻辑在 creatBean 中。在 creatBean 方法中,真正创建 Bean 的逻辑在 AbstractAutowiredCapableBeanFactory 的 doCreateBean 方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 根据指定bean使用对应的策略创建新的实例,如:工厂方法、构造函数自动注入、简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
......
// 是否需要提早曝光:单例、允许循环依赖、当前bean正在创建中,则允许提前曝光
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 为避免后期循环依赖,可以在bean初始化完成前将创建实例的 ObjectFactory 加入到三级缓存
// getEarlyBeanReference中主要应用SmartInstantiationAwareBeanPostProcessor处理器,AOP的动态织入就是在这里完成的
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 对bean进行填充,将各个属性值注入,其中可能存在依赖于其他bean的属性,则会递归初始依赖bean,这是循环依赖发生的位置
populateBean(beanName, mbd, instanceWrapper);
// 执行bean的初始化方法以及后置处理器,此时如果有aop增强的逻辑,得到的exposedObject就已经是增强后的对象了
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
if (earlySingletonExposure) {
// 这个我们可以看上面getSingleton方法,该方法的参数为false,就说明只允许去一级缓存和二级缓存获取,此时bean在创建中,一级缓存一定没有,就看二级缓存能不能获取到了
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 如果二级缓存获取到了,则说明提前执行了aop,因为执行了ObjectFactory才会从三级缓存移动到二级缓存
if (exposedObject == bean) {
// exposedObject就是要放入单例池中的对象,如果提前执行了aop,则将exposedObject对象替换为aop以后的对象
exposedObject = earlySingletonReference;
}
......
}
}
return exposedObject;
}
实际上,Spring 解决循环依赖的关键就在于 singletonFactories 这个三级缓存中。它里面缓存的是 ObjectFactory,它是解决问题的关键。
// 它可以将创建对象的步骤封装到ObjectFactory中 交给自定义的Scope来选择是否需要创建对象来灵活的实现scope
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
循环依赖总结
1、getBean(A) 先去单例池获取,单例池不存在,二级缓存获取,二级缓存不存在且允许提前访问,三级缓存中取,此时返回为空,开始加载 A
2、singletonsCurrentlyInCreation(A) 将 A 放入正在创建的 Map 中
3、new A(); 实例化 A
4、提前暴露 A,将 A 放入三级缓存,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
5、设置属性 populateBean(beanName, mbd, instanceWrapper);
6、发现 A 依赖 B,需要先创建 B
7、getBean(B)
8、先去单例池获取 B,单例池不存在,二级缓存获取,二级缓存不存在且允许提前访问,三级缓存中取,此时返回为空,开始加载 B
9、将 B 放入 singletonsCurrentlyInCreation() 的 Map 中
10、new B() 实例化 B
11、将 B 放入三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
12、设置属性 populateBean(beanName, mbd, instanceWrapper);
13、发现 B 依赖 A
14、getBean(A)
15、发现三级缓存中存在 A,getEarlyBeanReference(A, mbd, bean) 获取 A,同时把 A 放入二级缓存,删除三级缓存
16、执行 B 的 initializeBean 方法,执行 aop,获取增强以后的引用
17、B 创建完了,将 B 放入单例池冲
18、继续执行第 7 步,返回的 getBean(B)就是创建好的 B
19、接下来 A 初始化
20、因为 A 的三级缓存中的 getEarlyBeanReference(beanName, mbd, bean) 被 B 已经执行过了
21、A 就能从二级缓存中获取自己的引用
22、如果发现引用变了,此时 A 就指向二级缓存中的引用
23、将 A 放出单例池中
24、删除二级缓存和三级缓存