第12节 Spring源码之 Bean 的循环依赖

2023年 8月 1日 98.1k 0

循环依赖是 Spring 中经典问题之一,那么到底什么是循环依赖?简单说就是对象之间相互引用, 如下图所示:

Spring源码1-循环依赖现象.drawio (1).png

代码层面上很好理解,在 bean 创建过程中 class Aclass B 又经历了怎样的过程呢?

斩断循环依赖链路.jpg

可以看出形成了一个闭环,如果想解决这个问题,那么在属性填充时要保证不二次创建 A对象 的步骤,也就是必须保证从容器中能够直接获取到 B。

一、复现循环依赖问题

Spring 中默认允许循环依赖的存在,但在 Spring Boot 2.6.x 版本开始默认禁用了循环依赖

1. 基于xml复现循环依赖

  • 定义实体 Bean
public class A {

   private B b;

   public B getB() {
      return b;
   }

   public void setB(B b) {
      this.b = b;
   }
}
public class B {

   private A a;

   public A getA() {
      return a;
   }

   public void setA(A a) {
      this.a = a;
   }
}
  • 重写 customizeBeanFactory 方法,禁止循环依赖
public class NotAllowCircularXmlApplicationContext extends ClassPathXmlApplicationContext {

   public NotAllowCircularXmlApplicationContext(String... configLocations) throws BeansException {
      super(configLocations);
   }

   @Override
   protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
      // 禁止循环依赖
      super.setAllowCircularReferences(false);
      super.customizeBeanFactory(beanFactory);
   }
}
  • 配置 xml 文件



   

   
      
   
    
    
      
   

public class TestAnnoMain {

   public static void main(String[] args) {
      testCycleRef();
   }

   public static void testXmlCycleRef() {
      NotAllowCircularXmlApplicationContext ac = new NotAllowCircularXmlApplicationContext("application-cycle.xml");
      A bean = ac.getBean(A.class);
      System.out.println(bean);
   }
}   

运行结果:

image.png

2. 基于注解 @Autowired 复现循环依赖

  • 实体定义
@Component
public class C {

   @Autowired
   private D d;
}

@Component
public class D {

   @Autowired
   private C c;
}
  • 重写 AnnotationConfigApplicationContext,禁止循环依赖
public class NotAllowCircularAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext {

   public NotAllowCircularAnnotationConfigApplicationContext(Class... annotatedClasses) {
      super();
      register(annotatedClasses);

      // 禁止循环依赖
      super.setAllowCircularReferences(false);

      refresh();
   }
}
  • 运行结果
public class TestAnnoMain {

   public static void main(String[] args) {
      testCycleRef();
   }

   public static void testCycleRef() {
      NotAllowCircularAnnotationConfigApplicationContext ac = new NotAllowCircularAnnotationConfigApplicationContext(BeanConfig3.class);
      C bean = ac.getBean(C.class);
      System.out.println(bean);
   }
}   

image.png

二、在 Spring 中是如何解决该问题的呢?

Spring 中利用三级缓存解决循环依赖问题,缓存定义在 DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

// 一级缓存,保存 beanName 和 实例化、初始化好的完整 bean 对象
private final Map singletonObjects = new ConcurrentHashMap(256);

// 二级缓存,保存 beanName 和 未初始化的 bean 对象
private final Map earlySingletonObjects = new HashMap(16);

// 三级缓存,保存 beanName 和 lambda 表达式 () -> getEarlyBeanReference(beanName, mbd, bean)
private final Map

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论