前言
在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的。这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过spring的源码。但是说实话,spring的源码其实非常复杂的,研究起来并不是个简单的事情,所以我们此篇文章只是为了解释清楚Spring是如何解决循环依赖的这个问题。
什么是循环依赖?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:循环依赖有哪几种方式?
setter方式 - 单例
/**
* A服务类
*
* @author liudong
* @date 2021/12/20 14:48
*/
@Service("aService")
public class AService {
@Autowired
public BService bService;
}
/**
* B服务类
*
* @author liudong
* @date 2021/12/20 14:54
*/
@Service("bService")
public class BService {
@Autowired
public AService aService;
}
/**
* 配置类
*
* @author liudong
* @date 2021/12/20 14:59
*/
@Configuration
@ComponentScan
public class AppConfig {
}
/**
* 测试应用类
*
* @author liudong
* @date 2021/12/20 14:56
*/
public class TestApplication {
public static void main(String[] args) {
ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AService aService = (AService) annotationConfigApplicationContext.getBean("aService");
System.out.println(aService.bService);
BService bService = (BService) annotationConfigApplicationContext.getBean("bService");
System.out.println(bService.aService);
}
}
执行结果:
com.gaodun.spring.BService@1ed6388a
com.gaodun.spring.AService@5a45133e
虽然AService和BService产生了循环依赖的关系,但是没有异常抛出。说明被Spring解决了。但是能够通过“setAllowCircularReferences(false)”来禁用循环引用。这样也会抛出BeanCurrentlyInCreationException异常!测试代码如下:
/**
* 测试应用类
*
* @author liudong
* @date 2021/12/20 14:56
*/
public class TestApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.setAllowCircularReferences(false);
applicationContext.register(AppConfig.class);
applicationContext.refresh();
AService aService = (AService) applicationContext.getBean("aService");
System.out.println(aService.bService);
BService bService = (BService) applicationContext.getBean("bService");
System.out.println(bService.aService);
}
}
执行结果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aService': Requested bean is currently in creation: Is there an unresolvable circular reference?
需要注意的是,我们不能这么写:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); applicationContext.setAllowCircularReferences(false);
如果你这么写,程序执行完第一行代码,整个Spring容器已经初始化完成了,你再设置不允许循环依赖,也于事无补了。
setter方式 - prototype
使用@Scope(BeanDefinition.SCOPE_PROTOTYPE),进行多例的setter注入 ,每次请求都会建立一个实例对象。
/**
* A服务类
*
* @author liudong
* @date 2021/12/20 14:48
*/
@Service("aService")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class AService {
@Autowired
public BService bService;
}
/**
* B服务类
*
* @author liudong
* @date 2021/12/20 14:54
*/
@Service("bService")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class BService {
@Autowired
public AService aService;
}
执行结果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aService': Requested bean is currently in creation: Is there an unresolvable circular reference?
对于“prototype”作用域bean,Spring容器没法完成依赖注入,由于Spring容器不进行缓存“prototype”做用域的bean,所以没法提早暴露一个建立中的bean。
构造器循环依赖
/**
* A服务类
*
* @author liudong
* @date 2021/12/20 14:48
*/
@Service("aService")
public class AService {
public BService bService;
public AService(BService bService) {
this.bService = bService;
}
}
/**
* B服务类
*
* @author liudong
* @date 2021/12/20 14:54
*/
@Service("bService")
public class BService {
public AService aService;
public BService(AService aService) {
this.aService = aService;
}
}
执行结果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aService': Requested bean is currently in creation: Is there an unresolvable circular reference?
通过构造器注入构成的循环依赖是没法解决的,由于加入singletonFactories三级缓存的前提是执行了构造器,因此构造器的循环依赖无法解决,只能抛异常BeanCurrentlyInCreationException异常表示循环异常。
Spring是如何解决循环依赖的?
根据上文所述,Spring通过setter方式单例注入,是可以解决循环依赖的。关键源码如下:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// ...
/**
*一级缓存:单例池
*存放已经初始化的bean——成品
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
/**
*三级缓存:单例工厂的高速缓存
*存放生成bean的工厂
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
/**
*二级缓存:早期单例对象的高速缓存
*存放已经实例化但未初始化(未填充属性)的的bean——半成品
*/
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
//...
/**
*
*此处就是解决循环依赖的关键,这段代码发生在createBeanInstance以后,
*也就是说单例对象此时已经被建立出来的。这个对象已经被生产出来了,
*虽然还不完美(尚未进行初始化的第二步和第三步),
*可是已经能被人认出来了(根据对象引用能定位到堆中的对象),
*因此Spring此时将这个对象提早曝光出来让你们认识,让你们使用。
*/
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);
}
}
}
// ...
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先去一级缓存中获取如果获取的到说明bean已经存在,直接返回
Object singletonObject = this.singletonObjects.get(beanName);
//如果一级缓存中不存在,则去判断该bean是否在创建中,如果该bean正在创建中,就说明了,这个时候发生了循环依赖
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
//如果发生循环依赖,首先去二级缓存中获取,如果获取到则返回,这个地方就是获取aop增强以后的bean
singletonObject = this.earlySingletonObjects.get(beanName);
//如果二级缓存中不存在,且允许提前访问三级引用
if (singletonObject == null && allowEarlyReference) {
//去三级缓存中获取
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//如果三级缓存中的lambda表达式存在,执行aop,获取增强以后的对象,为了防止重复aop,将三级缓存删除,升级到二级缓存中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
// ...
}
以上便是spring解决循环依赖的核心代码,那么对于:A对象setter依赖B对象,B对象setter依赖A对象,Spring解决循环依赖的详细步骤如下:
- (1) A首先完成了初始化的第一步,而且将本身提早曝光到singletonFactories中。
- (2) 此时进行初始化的第二步,发现本身依赖对象B,此时就尝试去get(B),发现B尚未被create,因此走create流程,B在初始化第一步的时候发现本身依赖了对象A,因而尝试get(A),尝试一级缓存singletonObjects(确定没有,由于A还没初始化彻底),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,因为A经过ObjectFactory将本身提早曝光了,因此B可以经过ObjectFactory.getObject拿到A对象(虽然A尚未初始化彻底,可是总比没有好呀),B拿到A对象后顺利完成了初始化阶段一、二。彻底初始化以后将本身放入到一级缓存singletonObjects中。
- (3) 此时返回A中,A此时能拿到B的对象顺利完成本身的初始化阶段二、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,并且更加幸运的是,因为B拿到了A的对象引用,因此B如今hold住的A对象完成了初始化。