Spring 循环依赖理解

三级缓存定义

/** Cache of singleton objects: bean name --> bean instance */
/** 一级缓存:用于存放完全初始化好的 bean **/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of early singleton objects: bean name --> bean instance */
/** 二级缓存:存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** Cache of singleton factories: bean name --> ObjectFactory */
/** 三级级缓存:存放 bean 工厂对象,用于解决循环依赖 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

getBean() 流程

  1. getBean
  2. doGetBean
    1. getSingleton(beanName)—— 分别从一二三级缓存中查找
      protected Object getSingleton(String beanName, boolean allowEarlyReference) {
          // 首先从一级缓存中获取实例
         Object singletonObject = this.singletonObjects.get(beanName);
         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
               // 一级缓存获取不到 且 正在创建中
            synchronized (this.singletonObjects) {
                      // 尝试从二级缓存获取
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null && allowEarlyReference) {
                              // 允许循环依赖,则尝试从三级缓存获取
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                                  // 回调三级缓存工厂的 getObject(),创建(代理若有必要)bean
                     singletonObject = singletonFactory.getObject();
                                  // 存入二级缓存,删除三级缓存
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
         return (singletonObject !=NULL_OBJECT? singletonObject : null);
      }
      

      注意会调用三级缓存中的

      singletonFactory = this.singletonFactories.get(beanName)

      返回 singletonFactory.getObject() 存入 earlySingletonObjects 缓存,因为三级缓存中的bean不完整,已实例化但还没有进行依赖注入和初始化,所以也称为提早曝光。

    2. getSingleton 传入创建 bean 的 function,在其内部回调这个 function 创建 bean

      getSingleton(beanName, new ObjectFactory<Object>() {
         @Override
         public Object getObject() throws BeansException {
            try {
               return createBean(beanName, mbd, args);
            }
      
      1. createBean
        1. resolveBeforeInstantiation // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance

          通过这个接口自定义实现来替换bean自身的实例化,在bean实例化之前就直接返回InstantiationAwareBeanPostProcessor

        2. doCreateBean

          1. createBeanInstance 确定构造方法并完成实例化
          2. addSingletonFactory 将getObject function 放入三级缓存,删除二级缓存,这里 getEarlyBeanReference 实际上会返回代理对象(如果需要代理)。这里的
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
               @Override
               public Object getObject() throws BeansException {
                  return getEarlyBeanReference(beanName, mbd, bean);
               }
            
            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);
                  }
               }
            }
            
          3. populateBean 注入依赖,递归 getBean

          4. initializeBean

            1. invokeAwareMethods
            2. applyBeanPostProcessorsBeforeInitialization
            3. invokeInitMethods
            4. applyBeanPostProcessorsAfterInitialization

              调用postProcessAfterInitialization 生成代理AOP对象

      2. addSingleton 实例化完成后,加入一级缓存,同时删掉二三级缓存

问题

一、循环依赖为什么需要三级缓存?

A → B

B → A

实际上A实例化之后放到缓存,在B需要时从缓存取出,同样可以解决循环依赖。

但 Spring 考虑的是在 AOP 存在的情况下,正常情况下 AOP 发生在属性注入后,初始化时。但在循环依赖的case下,如果不用三级缓存提前暴露bean的引用,就会出现A引用B的原始对象,但是B在完成bean的生命周期后被代理类替换了,产生了不一致。

所以 Spring 采用提前暴露 bean 的生成代理的 reference function。若存在循环依赖则会回调这个function,提前生成代理对象;反之则走正常的 AOP 流程。

二、为什么 prototype 不支持循环依赖?

prototype没有缓存,那么 A → B → A … 就会死循环

三、还是存在一个问题,为什么 Spring 要设计成先初始化再生成代理?而不是直接生成代理?

Bean 生命周期及拓展点

利用拓展点的相关实现

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

Scroll to Top