Spring解决循环依赖的原理

什么是循环依赖?

简单来说就是A、B两个对象互相依赖,A中有B,B中有A。

循环依赖分类:

  • 构造器循环依赖

    //A类中引用B类实例
    class A {
      B b;
      public A(B b){
        this.b = b;
      }
    }
    -----------------
    //B类中引用A类实例
    class B {
      A a;
      public B(A a) {
        this.a = a;
      }
    }
    

    像上面这种构造器循环引用是无解的,即使强如Spring这样的框架。

  • setter方法循环依赖(单实例)

    //A类中引用B类实例
    class A {
      B b;
      public void setB(B b) {
        this.b = b;
      }
    }
    -----------------
    //B类中引用A类实例
    class B {
      A a;
      public void setA(A a) {
        this.a = a;
      }
    }
    

    Spring框架中默认是支持这种setter方法的循环依赖的,因为setter方法的循环依赖是允许创建一个“半成品”(依赖的属性值为null)的bean。

Spring解决循环依赖原理

Spring在解决循环依赖的时候使用到了三级缓存,所谓的三级缓存也就是DefaultSingletonBeanRegistry类中的三个map:

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

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

//二级缓存
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

我们以UserServiceIndexService两个类为例,说明整个循环引用的解决流程

//IndexService中引用UserService对象
public class IndexService {
	private UserService userService;
	public UserService getUserService() {
		return userService;
	}
	public void setUserService(UserService userService) {
		this.userService = userService;
	}
	public IndexService() {
		System.out.println("IndexService初始化");
	}
}
----------------------------------------------------------------------------
//UserService中引用IndexService对象
public class UserService {
	private IndexService indexService;
	public IndexService getIndexService() {
		return indexService;
	}
	public void setIndexService(IndexService indexService) {
		this.indexService = indexService;
	}
	public UserService() {
		System.out.println("userService 初始化");
	}
}

在xml中配置两个bean

<!--application.xml-->
<bean id="indexService" class="top.cocoawork.service.IndexService">
	<property name="userService" ref="userService"></property>
</bean>
<bean id="userService" class="top.cocoawork.service.UserService">
	<property name="indexService" ref="indexService"></property>
</bean>

使用当前配置文件创建IoC容器

public class SpringApplication {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		IndexService indexService = applicationContext.getBean(IndexService.class);
		System.out.println(indexService);
	}
}

开启调试,让我们直奔目的,顺着调用链,找到IoC容器创建bean的地方:

1.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(String configLocation) ;
2.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent);
3.AbstractApplicationContext#refresh();
4.AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory);
5.DefaultListableBeanFactory#preInstantiateSingletons();
6.AbstractBeanFactory#getBean(String name);
7.AbstractBeanFactory#doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly);
8.DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference);

上面第8步其实已经开始创建bean了,从这里开始慢慢调试吧💣💣,我总结下其流程:

  • IndexServiceUserService创建流程:
    1. 尝试从一级缓存中获取IndexService实例,获取失败
    2. IndexService标记为创建状态
    3. 实例化IndexService,并将IndexService放入到三级缓存中
    4. 为已经实例化的IndexService填充属性UserService userService
    5. 从一级缓存中获取UserService实例,显然不能获取到
      1. 标记UserService为创建状态
      2. 再次从一级缓存中获取UserService实例,再次获取失败
      3. 创建UserService实例,然后放到三级缓存中
      4. UserService实例填充属性IndexService indexService
      5. 从一级缓存中获取IndexService实例,获取失败
      6. 从二级缓存中获取IndexService实例,获取失败
      7. 从三级缓存中获取IndexService实例(三级缓存存储的为对象工厂,此时调用对象工厂增强)
      8. 将获取到的IndexService实例放到二级缓存中
      9. 从三级缓存中删除IndexService实例
      10. IndexService实例填充到UserService实例中
      11. UserService实例添加到一级缓存
      12. 从三级缓存中将UserService实例移除
    6. 此时已经得到了完整生命周期的UserService对象了,将其填充到IndexService实例中
    7. IndexService实例添加到一级缓存
    8. IndexService实例从二级缓存中移除

到此,已经完成IndexServiceUserService的创建。

在以上过程中,三级缓存的三个map中的对象变化如下:

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
	一级缓存:
	添加 userService:   UserService实例(5-11步骤)
	添加 indexService: IndexService实例 (7步骤)


private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
	三级缓存:
	添加 indexService:   ObjectFactory(3步骤)
	添加 userService:    ObjectFactory(5-3步骤)
	移除 indexService:   ObjectFactory(5-9步骤)
	移除 userService:    ObjectFactory(5-12步骤)

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
	二级缓存:
	添加 indexService: IndexService实例(5-8步骤)
	移除 indexService: IndexService实例 (8步骤)

以上就是Spring中解决循环依赖的整个对象创建流程,这是耗费了一上午调试得出的结论。
其最最核心的原理就是,将实例化为初始化的半成品bean缓存起来,让需要引用它的对象先得到它,待引用它的对象完成初始化后,再用已经完成初始化的对象来填充这个半成品bean,从而都完成初始化完成bean创建。不得不说,Spring牛逼!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值