什么是循环依赖?
Spring中的循环依赖指的是A类注入了B类属性,而B类又依赖了A类属性。如果有这种情况,那么在创建A对象
的时候,B还没有创建,如果去创建B,那A还没有创建。这种情况,Spring是如何解决的呢?
循环依赖的两种方式
同spring的依赖注入方式一样,setter方法和构造器方法。
setter方式
首先我们先用setter方式来表示这种循环依赖关系。两个bean:OrderService.java和UserService.java
@Component
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
@Autowired
private UserService userService;
}
@Component
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
@Autowired
private OrderService orderService;
}
创建OrderService对象
好,debug源码,在spring创建对象前,我们先看一下Spring容器中beanDefinitionMap中都有什么bean定义和顺序。
可以看到0-5是Spring内部的后置处理器和监听工厂等内置bean,6是我的配置类,以上不需要关注。7和8就是我们循环依赖的bean。按顺序我们就先关注orderService的实例化过程。
实例化的流程节点这里就不详细介绍了,这里主要着重研究的是如何解决循环引用,所以部分代码就略过了。
首先就是在实例化时的getSingleton方法内,它走了beforeSingletonCreation方法。他本质就是做个标记,表示此类正在被实例化,将此beanName添加到singletonsCurrentlyInCreation中,这里稍微有个印象即可。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 略过...
beforeSingletonCreation(beanName);
// 略过。。。
}
return singletonObject;
}
}
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
紧接着跟源码就会到这里doCreateBean方法,此方法在spring执行中是比较重要的方法,它主要是实例化bean对象和初始化bean对象,实例化bean就是通过构造器去创建对象,初始化bean就是向此对象注入依赖。这里我们要关注的是实例化bean对象后的addSingletonFactory方法。
要执行addSingletonFactory方法,它的条件就是单例且bean工厂允许循环依赖(默认允许)和isSingletonCurrentlyInCreation(当前对象正在被创建,也就是之前提到的singletonsCurrentlyInCreation集合要包含此beanName)。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 实例化当前bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 略过...
// 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) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 初始化bean
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// 略过...
}
// 略过...
return exposedObject;
}
addSingletonFactory方法就是将此bean对象提前缓存起来。它的参数是beanName和ObjectFactory。spring采取的是lambda表达式来表示给定的ObjectFactory参数。
看这个方法内部,这里要提到spring的三种缓存,网上其它博客把它称为一/二/三级缓存,也可以,能方便理解些。
- 一级缓存:singletonObjects-这里存放的是spring已经实例化且初始化完毕的bean对象。
- 二级缓存:earlySingletonObjects-这里存放的是已经实例化但没有初始化的bean对象。
- 三级缓存:singletonFactories-这里存放的是ObjectFactory对象,状态节点类似二级缓存,但本质上它ObjectFactory对象内部在调用getObject时会执行一个后置处理器,这里暂时不用管。
所以这里我们就知道了当前的半成品被封装成了ObjectFactory放入到了三级缓存singletonFactories中。
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);
}
}
}
查找OrderService的依赖UserService,创建UserService对象
再往下就会执行populateBean方法,此方法会查找依赖,注入依赖。
首先就是查找依赖过程,由于我orderService类内部依赖了UserServiec,所以,它会在执行doResolveDependency中的findAutowireCandidates方法时,在容器内部匹配到同类型的UserService,后边就会通过getBean方法获取UserService对象。
这里开始我们就要研究它如何创建UserService了。
首先创建UserService的步骤同上边一样,也就是实例化bean后,将半成品对象封装成ObjectFactory也放入singletonFactories中,这里我们会发现singletonFactories就有了两个ObjectFactory对象。
查找UserService的依赖OrderService,再次创建获取OrderService对象
然后就会再次执行populateBean方法查找UserService它的依赖,当他查找匹配到OrderService时,就会再次执行getBean去获取OrderService对象,好,让我们跟着代码走到这里。是不是有点绕,又回到了当初的起点。但是不同的是在spring工厂内部肯定是维护了一些缓存,比如说上边的singletonFactories等。同时你要知道,到这里其实spring还没有完全初始化我们的bean对象,它仅仅是实例化而已。
好,进入doGetBean方法。这里主要关注的就是getSingleton方法。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 尝试从缓存获取指定的bean对象
Object sharedInstance = getSingleton(beanName);
// 略过...
}
注意这里,这里就会从缓存中获取OrderService这个bean对象,逻辑并不复杂。
- 首先会尝试从一级缓存singletonObjects中获取,前面我们说过,这里存的是spring完全初始化的对象,所以肯定是没有的。
- 然后就会尝试从二级缓存earlySingletonObjects中获取,这里我们之前都没有操作过,所以肯定没有。
- 再之后就会尝试从三级缓存singletonFactories中获取,因为我们之前走了两遍getBean,都实例化但没有完全初始化,所以这里就会有两个ObjectFactory对象。并且包含orderService对应的ObjectFactory。
- 紧接着它会执行getObject方法获取半成品对象。
- 获取到半成品orderService后,就会将此对象放入二级缓存earlySingletonObjects中,并且删除三级缓存中的对象。
执行到这,就会返回了我们UserService的依赖–OrderService对象,大功告成!!!
找到bean后,就会返回到UserService的初始化流程中,通过反射注入到UserService中。这样UserService就找到了所有依赖,进入到创建UserService对象的结尾操作。getSingleton方法结尾进行标记删除和重新缓存操作。
完成UserService的创建
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 略过
// 创建对象完成,从singletonsCurrentlyInCreation删除此beanName。
afterSingletonCreation(beanName);
// 将bean对象添加到一级缓存
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
删除singletonsCurrentlyInCreation中的beanName,表示创建完毕。
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
这里就会将此UserService对象缓存到一级缓存singletonObjects中,同时删除二级缓存earlySingletonObjects和三级缓存singletonFactories中的对象。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
好的,这里基本就结束了循环依赖的核心流程。后边就会返回UserService对象,又回到了创建OrderService的流程中,同上边一样,将返回的UserService对象通过反射注入到OrderService内。
总结setter方式的循环依赖解决思路
本质上,spring就是利用了java对象的地址引用特性。实例化OrderService后,在实例化UserService时,虽然OrderService还没有完全初始化,但可以先保留其地址引用,后续OrderService的任何初始化操作都不会影响地址引用的改变。所以Spring可以将其保存在三级或二级缓存内,进而可以解决循环依赖的问题。
构造器方式
代码如下
OrderService.java
@Component
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
private UserService userService;
@Autowired
public OrderService(UserService userService) {
this.userService = userService;
}
}
UserService.java
@Component
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
private OrderService orderService;
@Autowired
public UserService(OrderService orderService) {
this.orderService = orderService;
}
}
直接输出结论吧,构造器方式不支持循环依赖。他会爆出如下异常。
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1131)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1058)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:819)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:725)
... 27 more
为什么构造器方式不支持循环依赖
首先还是先创建OrderService对象,但因为这里在构造器上添加了@Autowired注解,所以在spring实例化流程上,他会选择添加了@Autowired注解的构造器去实例化对象,然后会去解析这个构造器上的参数对象UserService。
这其中的过程与setter类似,在解析UserService时,又发现了它依赖OrderService对象,就会去再一次getBean这个OrderService对象。
不同的是这次再执行getSingleton(beanName)方法时,内部的三级缓存singletonFactories是没有OrderService的ObjectFactory的。所以,他后续会再一次执行创建bean的流程,但是在执行到下方代码beforeSingletonCreation方法时,就会产生异常。其实这个方法我们之前就注意过,功能是对当前创建的对象标记状态。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 略过
// 对当前bean创建进行打标,标记状态--正在创建
beforeSingletonCreation(beanName);
// 略过
}
上面产生异常的原因就是singletonsCurrentlyInCreation已经包含了orderService,所以无法再次添加,也就意味着不允许再一次创建单例对象,不然就死循环了。
为什么spring不能仿造setter那样,在构造器执行时也放入三级缓存呢?
因为它不可能产生对象的,解析构造器参数就报错了,我们的bean的构造器还没有执行,又怎么会创建对应的对象!不可能加入三级缓存内。
总结
循环依赖大概就是以上的原理了,过程挺绕的,如果看代码,需要提前了解spring的IOC流程的,不然很容易深陷其中,最后放弃。setter方式可以解决循环依赖,构造器方式是无法解决的。