Bean-循环依赖
# 什么是循环依赖?
循环依赖:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用(闭环)。
例:第一种情况:两个对象之间的直接依赖:
第二种情况:多个对象之间的间接依赖
注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。
# 怎么检测是否存在循环依赖
检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标记,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。
# 循环依赖_效果演示
Spring中循环依赖场景有:
- 构造器的循环依赖
- field属性的循环依赖
# (1)构造器的循环依赖
构造器的循环依赖就是在构造器中有属性循环依赖,如下:
public class testService1 {
private testService2 service_2;
public testService1(testService2 service_2) {
this.service_2 = service_2;
}
public void aTest(){
System.out.println("testService1,注入了属性" + service_2 );
}
}
public class testService2 {
private testService1 service_1;
public testService2(testService1 service_1) {
this.service_1 = service_1;
}
public void bTest(){
System.out.println("testService2,注入了属性" + service_1 );
}
}
applicationContext.xml
<!--testService1通过构造方法注入testService2-->
<bean id="testService1" class="com.itheima.cyclic.testService1">
<constructor-arg name="service_2" ref="testService2"/>
</bean>
<!--testService2通过构造方法注入testService1-->
<bean id="testService2" class="com.itheima.cyclic.testService2">
<constructor-arg name="service_1" ref="testService1"/>
</bean>
测试代码:
/**
* 循环依赖效果演示
* @param args
*/
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("classpath:applicationContext-cyclic.xml");
testService1 testService1 = (com.itheima.cyclic.testService1) applicationContext.getBean("testService1");
testService1.aTest();
}
测试结果:产生了循环依赖
# (2)field属性的循环依赖
public class TestService1 {
private TestService2 testService2;
public void setTestService2(TestService2 testService2) {
this.testService2 = testService2;
}
public void aTest(){
System.out.println("testService1,注入了" + testService2 );
}
}
public class TestService2 {
private TestService1 testService1;
public void setTestService1(TestService1 testService1) {
this.testService1 = testService1;
}
public void aTest(){
System.out.println("testService1,注入了属性" + testService1 );
}
}
applicationContext.xml
<bean id="testService1" class="com.itheima.cyclic.TestService1">
<property name="testService2" ref="testService2"/>
</bean>
<bean id="testService2" class="com.itheima.cyclic.TestService2">
<property name="testService1" ref="testService1" />
</bean>
测试结果:
# 结论:
构造器注入引起的循环依赖(不能解决)
单例Bean的Setter注入产生的循环依赖(能解决)
# Spring怎么解决循环依赖
Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或则属性是可以延后设置的(但是构造器必须是在获取引用之前)。
# 三级缓存
Spring为了解决单例的循环依赖问题,使用了三级缓存(三个map)
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
# Spring解决循环依赖流程图
# 循环依赖_源码剖析
1.使用getBean(java.lang.Class)从IOC中获取bean信息,实际上在IOC容器通过扫描包或加载XML后也会循环调用getBean(...)进行Bean的首轮实例化。
下面来详细了解下getBean(...)中对于循环依赖的处理:
//org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
//doGetBean是getBean方法的实际逻辑方法,这里只贴出了相关的部分代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//处理bean名称的规范问题
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//从缓存中获取bean实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//省略...
try {
//获取beanName对应的BeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//省略...
// Create bean instance.
//根据bean的作用域来创建bean实例
if (mbd.isSingleton()) {
//创建单例模式的bean
sharedInstance = getSingleton(beanName, () -> {
try {
//单例的bean实例化方法
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);
}
else if (mbd.isPrototype()) {
//创建原型模式bean
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}else {
//创建其他模式bean
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
//省略...
return (T) bean;
}
上述doGetBean大致做了几个步骤:
1.尝试根据beanName从缓存中获取获取bean对象
2.若获取到缓存对象则执行getObjectForBeanInstance(...)后返回bean信息
3.若没有获取到缓存对象(首次创建),则根据bean的作用域类型来采取不同方式创建bean(这里默认为单例模式),然后再执行getObjectForBeanInstance(...)后返回bean信息
其中涉及到循环依赖的处理有getSingleton(beanName)先获取缓存对象:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 1.从单例对象缓存(1级缓存)中获取beanName对应的单例对象
Object singletonObject = this.singletonObjects.get(beanName);
// 2.如果单例对象缓存(1级缓存)中没有,并且该beanName对应的单例bean正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 3.从早期单例对象缓存中(二级缓存)获取单例对象(之所称成为早期单例对象,是因为earlySingletonObjects里
// 的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作)
singletonObject = this.earlySingletonObjects.get(beanName);
// 4.如果在早期单例对象缓存中(二级缓存)也没有,并且允许创建早期单例对象引用
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 6.从单例工厂缓存中(三级缓存)获取beanName的单例工厂
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 再次从二级缓存中获取,重复校验
singletonObject = this.earlySingletonObjects.get(beanName);
// 为null
if (singletonObject == null) {
// 6.再从单例工厂缓存中(三级缓存)获取beanName的单例工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 7.如果存在单例对象工厂,则通过工厂创建一个单例对象
singletonObject = singletonFactory.getObject();
// 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
// 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
分别从一级缓存、二级缓存、三级缓存中进行查找
2.这里我们的bean按照单例模式,走首次创建路径createBean(beanName, mbd, args);,而createBean(beanName, mbd, args);中真正的逻辑方法是doCreateBean(...),下面我们看下doCreateBean(...)的方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//根据beanName将当前对象从未完成实例化列表缓存中移除并返回
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//若未完成实例化列表缓存中没有数据则创建一个空对象
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
//省略...
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//将bean写入提前暴露的缓存中(此时的bean刚实例化,还没有对其属性进行赋值处理)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//将beandefinition中的属性写入对应的instanceWrapper对象实例中
//依赖循环就是在这里处理的
populateBean(beanName, mbd, instanceWrapper);
//如果exposedObject对象有实现一些aware、init接口则初始化这些接口
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
//省略...
return exposedObject;
}
doCreateBean(...)的主要逻辑有以下几步:
- 创建一个bean的包装对象instanceWrapper(实际为Class.forName(className).newInstance()创建,有兴趣自行可跟踪代码)
- 通过addSingletonFactory(...)将刚实例化的对象放入缓存中
- 在populateBean(...)中处理bean对象的依赖属性(在这里递归调用其他依赖的bean)
- 在initializeBean(...)中调用对象的一些初始化接口(如实现InitializingBean),并返回结果bean
涉及循环依赖的处理有addSingletonFactory(...)和populateBean(...)两部分,我们先看下addSingletonFactory(...)将bean加入缓存中:
//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//没有创建过beanName的bean则加入缓存
if (!this.singletonObjects.containsKey(beanName)) {
//存储在singletonFactories中,在getSingleton(...)中获取调用
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
//参数ObjectFactory<?> singletonFactory是一个函数式接口对象
//内容为() -> getEarlyBeanReference(beanName, mbd, bean)
//调用singletonFactory会执行getEarlyBeanReference(beanName, mbd, bean),返回bean的首次创建对象
//实际上会在获取缓存对象的getSingleton(...)中调用 singletonFactory.getObject();
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
//忽略...
return exposedObject;
}
3.而populateBean(...)
是根据BeanDefinition将属性赋值到刚创建的对象中,主要的逻辑在applyPropertyValues(...)
中执行,大致代码如下:
//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
//省略...
//创建属性解析器(主要完成属性值的处理,包括依赖其他bean的创建)
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
//获取属性名称
String propertyName = pv.getName();
Object originalValue = pv.getValue();
//使用解析器解析不同类型的值
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
//将值包装到deepCopy的list中
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// Possibly store converted value in merged bean definition,
// in order to avoid re-conversion for every created bean instance.
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
// Set our (possibly massaged) deep copy.
try {
//将属性赋值到对象中
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
主要逻辑是如下:
- 创建属性解析器valueResolver, 之后循环BeanDefinition中的属性列表,使用解析器对每个property进行实际值的解析(保存创建依赖bean对象)
- 根据属性的名称将属性值赋值到对象中
4.涉及到循环依赖的逻辑是valueResolver.resolveValueIfNecessary(pv, originalValue),使用属性解析器获取property的实际内容,下面我们看下如何解析property的(只看依赖其他bean的property):
//org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary
@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// We must check each value to see whether it requires a runtime reference
// to another bean to be resolved.
//处理依赖其他bean的property
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
//省略...
}
//详细处理逻辑
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
Object bean;
//获取依赖bean名称
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
//依赖是否属于父容器
if (ref.isToParent()) {
if (this.beanFactory.getParentBeanFactory() == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Can't resolve reference to bean '" + refName +
"' in parent factory: no parent factory available");
}
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
}
else {
//嵌套调用IOC容器的getBean方法
bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
}
if (bean instanceof NullBean) {
bean = null;
}
return bean;
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}
上述逻辑比较清晰简单,就是根据依赖的beanName嵌套调用this.beanFactory.getBean(refName)
去创建所依赖对象,创建完成后返回该bean信息。
总结: 到这里我们就可以知道spring是如何处理依赖循环的了:
(1)调用getBean(...)方法创建一个bean,前先从缓存getSingleton(...)中获取对象信息
(2)若是没有缓存,则首次创建后将其对象加入到三级缓存中
(3)之后对创建的对象进行属性填充populateBean(...),填充过程中创建属性解析器对bean的属性进行处理
(4)若属性类型依赖其他的bean,则会嵌套调用IOC容器的getBean方法去创建所依赖的bean对象,直到出现从缓存中获取到对象后跳出嵌套逻辑,才可以完成整个bean的属性赋值过程。
# 循环依赖经典面试题
# 【Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?】
答:只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题
# 【三级缓存中为什么要添加ObjectFactory
对象,而不是直接保存实例对象?】
答:因为假如想对添加到三级缓存中的实例对象进行增强,直接用实例对象是行不通的。
# 【构造器注入注入的循环依赖为什么无法解决?】
答:源码中对于解决逻辑的第一句话:“我们先用构造函数创建一个 “不完整” 的 bean 实例”,从这句话可以看出,构造器循环依赖是无法解决的,因为当构造器出现循环依赖,我们连 “不完整” 的 bean 实例都构建不出来。