Spring-bean循环依赖

深藏阁楼爱情的钟 2022-02-25 05:16 397阅读 0赞

循环依赖指的是两个或以上bean相互存在引用。

Spring中循环依赖的几种情况:
1.构造器参数循环依赖;无法解决,只能避免
2.setter方式,单例;
3.setter方法,prototype原型;无法解决,只能避免

在bean初始化一文中,可以知道,创建bean需要经过一下几个步骤:
1.实例化bean;
2.populate bean,填充bean的属性;
3.initialize bean,初始化bean;

对于作用域为singleton的bean,创建过程中以及创建完成会缓存该bean;
从DefaultSingletonBeanRegistry的源码中可以看出:

  1. /** Cache of singleton objects: bean name --> bean instance */
  2. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  3. /** Cache of singleton factories: bean name --> ObjectFactory */
  4. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  5. /** Cache of early singleton objects: bean name --> bean instance */
  6. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  7. //标记bean正在创建,创建完成后会remove
  8. /** Names of beans that are currently in creation */
  9. private final Set<String> singletonsCurrentlyInCreation =
  10. Collections.newSetFromMap(new ConcurrentHashMap<>(16));

spring容器通过三级缓存解决循环依赖问题。
容器在创建单例bean前,会先判断缓存中是否存在;
AbstractBeanFactory#doGetBean:

  1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
  2. @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  3. final String beanName = transformedBeanName(name);
  4. Object bean;
  5. // Eagerly check singleton cache for manually registered singletons.
  6. //从缓存中获取
  7. Object sharedInstance = getSingleton(beanName);
  8. if (sharedInstance != null && args == null) {
  9. if (logger.isDebugEnabled()) {
  10. if (isSingletonCurrentlyInCreation(beanName)) {
  11. logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
  12. "' that is not fully initialized yet - a consequence of a circular reference");
  13. }
  14. else {
  15. logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
  16. }
  17. }
  18. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  19. } else {
  20. //不存在,创建bean
  21. ...
  22. }
  23. }

再看看getSingleton方法:

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. Object singletonObject = this.singletonObjects.get(beanName);
  3. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  4. synchronized (this.singletonObjects) {
  5. singletonObject = this.earlySingletonObjects.get(beanName);
  6. if (singletonObject == null && allowEarlyReference) {
  7. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  8. if (singletonFactory != null) {
  9. singletonObject = singletonFactory.getObject();
  10. this.earlySingletonObjects.put(beanName, singletonObject);
  11. this.singletonFactories.remove(beanName);
  12. }
  13. }
  14. }
  15. }
  16. return singletonObject;
  17. }

先从一级缓存singletonObjects获取,没有则从earlySingletonObjects获取,再没有就从singletonFactories获取,如果有,则移到二级缓存earlySingletonObjects;

如果缓存没有,则创建bean。bean在实例化bean之后,会将bean添加到singletonObjects中,并将bean从singletonFactories和earlySingletonObjects中移除,DefaultSingletonBeanRegistry#addSingleton:

  1. /**
  2. * Add the given singleton factory for building the specified singleton
  3. * if necessary.
  4. * <p>To be called for eager registration of singletons, e.g. to be able to
  5. * resolve circular references.
  6. * @param beanName the name of the bean
  7. * @param singletonFactory the factory for the singleton object
  8. */
  9. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  10. Assert.notNull(singletonFactory, "Singleton factory must not be null");
  11. synchronized (this.singletonObjects) {
  12. if (!this.singletonObjects.containsKey(beanName)) {
  13. this.singletonFactories.put(beanName, singletonFactory);
  14. this.earlySingletonObjects.remove(beanName);
  15. this.registeredSingletons.add(beanName);
  16. }
  17. }
  18. }

这是解决循环依赖的关键,该方法调用在createBeanInstance之后,populateBean之前,它的作用是提前暴露对象,但该bean未完成属性填充以及初始化。
比如A和B存在循环依赖,bean A在实例化后,将自己保存到singletonFactories,此时进行下一步填充属性,发现需要引用到B,接着容器创建bean B,在实例化bean B后,填充属性时发现依赖到A,此时尝试从容器中获取A,由于此时A初始化尚未完成,但是A提前暴露了对象,所以可以从singletonFactories中提前获取A对象引用,接着B完成自身初始化,完成后再返回A,继续进行属性填充及初始化。

从上面也可以知道,为什么构造器参数循环依赖不行,因为要先实例化bean。容器也不能解决原型bean的循环依赖问题,因为容器没有缓存原型bean,也就不能提前暴露对象,只能去避免。

参考

https://blog.csdn.net/u010853261/article/details/77940767
https://blog.csdn.net/u010644448/article/details/59108799#comments

发表评论

表情:
评论列表 (有 0 条评论,397人围观)

还没有评论,来说两句吧...

相关阅读

    相关 spring循环依赖

    Bean的生命周期 这里不会对Bean的生命周期进行详细的描述,只描述一下大概的过程。 Bean的生命周期指的就是:在Spring中,Bean是如何生成的? > 被Sp

    相关 5.循环依赖

    循环依赖就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个