Spring的三级缓存

Spring三级缓存

对象创建的过程

  • spring的三级缓存分别是
	// 从上至下 分表代表这“三级缓存”
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
  1. singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
  2. earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
  3. singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
  • 对象创建流程
  1. AbstractBeanFactory 中的 doGetBean()方法

  2. DefaultSingletonBeanRegistry中的 getSingleton()方法

    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) { 
    						singletonObject = singletonFactory.getObject();
    						this.earlySingletonObjects.put(beanName, singletonObject);
    						this.singletonFactories.remove(beanName);
    					}
    				}
    			}
    		}
    		return singletonObject;
    }
    
    • 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
    • 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
    • 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)此处的移动保证了,之后在init时候仍然是同一个对象
  3. AbstractAutowireCapableBeanFactory.createBean/doCreateBean()

    • 实例化
    	// 使用构造器/工厂方法 instanceWrapper是一个BeanWrapper
    	instanceWrapper = createBeanInstance(beanName, mbd, args);
    	// 此处bean为"原始Bean" 也就是这里的A实例对象:A@1234
    	final Object bean = instanceWrapper.getWrappedInstance();
    
    • 添加到三级缓存
    // 允许暴露,就把A绑定在ObjectFactory上,注册到三级缓存`singletonFactories`里面去保存着
    // Tips:这里后置处理器的getEarlyBeanReference方法会被促发,自动代理创建器在此处创建代理对象(注意执行时机 为执行三级缓存的时候)
    if (earlySingletonExposure) { 
    	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    //注意 此时加入的是一个factory没有执行 
    
  4. 属性赋值

	//此时候上面说到的getEarlyBeanReference方法就会被执行。这也解释为何我们@Autowired是个代理对象,而不是普通对象的根本原因
	
	populateBean(beanName, mbd, instanceWrapper);
  • 解决循环依赖,在获取单例对象时

    singletonFactory.getObject()调用了

    public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
            implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { 
        private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
                
        @Override
        public Object getEarlyBeanReference(Object bean, String beanName) { 
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            this.earlyProxyReferences.put(cacheKey, bean);
            /* 这里,主要就是看看到底要不要生成代理对象,要的话,就生成,不要就算了,另外,做了个标记:在earlyProxyReferences加了当前bean的key,表示:当前bean,已经被getEarlyBeanReference方法处理过了。 /至于,最终到底有没有生成代理对象,另说。毕竟调用wrapIfNecessary也不是说,一定就满足切面,要生成代理对象。 可能返回的仍然是原始对象。*/
            return wrapIfNecessary(bean, beanName, cacheKey);
        }        
    }   
    

    此处的SmartInstantiationAwareBeanPostProcessor继承自BeanPostProcessor

    BeanPostProcessor接口会在init阶段生成对对象的代理,getCacheKey保证不会重复生成代理对象

    1. 初始化对象

      如果有代理会检查是否发生了循环依赖

      exposedObject = initializeBean(beanName, exposedObject, mbd);
      
      	... // 至此,相当于A@1234已经实例化完成、初始化完成(属性也全部赋值了~)
      	// 这一步我把它理解为校验:校验:校验是否有循环引用问题~~~~~
      
      	if (earlySingletonExposure) { 
      		// 注意此处第二个参数传的false,表示不去三级缓存里singletonFactories再去调用一次getObject()方法了~~~
      		// 上面建讲到了由于B在初始化的时候,会触发A的ObjectFactory.getObject() 所以a此处已经在二级缓存earlySingletonObjects里了
      		// 因此此处返回A的实例:A@1234
      		Object earlySingletonReference = getSingleton(beanName, false);
      		if (earlySingletonReference != null) { 
      		
      			// 这个等式表示,exposedObject若没有再被代理过,这里就是相等的
      			// 显然此处我们的a对象的exposedObject它是没有被代理过的 所以if会进去~
      			// 这种情况至此,就全部结束了~~~
      			if (exposedObject == bean) { 
      				exposedObject = earlySingletonReference;
      			}
      	
      			// 继续以A为例,比如方法标注了@Aysnc注解,exposedObject此时候就是一个代理对象,因此就会进到这里来
      			//hasDependentBean(beanName)是肯定为true,因为getDependentBeans(beanName)得到的是["b"]这个依赖
      			else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 
      				String[] dependentBeans = getDependentBeans(beanName);
      				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
      
      				// A@1234依赖的是["b"],所以此处去检查b
      				// 如果最终存在实际依赖的bean:actualDependentBeans不为空 那就抛出异常 证明循环引用了~
      				for (String dependentBean : dependentBeans) { 
      					// 这个判断原则是:如果此时候b并还没有创建好,this.alreadyCreated.contains(beanName)=true表示此bean已经被创建过,就返回false
      					// 若该bean没有在alreadyCreated缓存里,就是说没被创建过(其实只有CreatedForTypeCheckOnly才会是此仓库)
      					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 
      						actualDependentBeans.add(dependentBean);
      					}
      				}
      				if (!actualDependentBeans.isEmpty()) { 
      					throw new BeanCurrentlyInCreationException(beanName,
      							"Bean with name '" + beanName + "' has been injected into other beans [" +
      							StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
      							"] in its raw version as part of a circular reference, but has eventually been " +
      							"wrapped. This means that said other beans do not use the final version of the " +
      							"bean. This is often the result of over-eager type matching - consider using " +
      							"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
      				}
      			}
      		}
      	}
      

是否可以没有二级缓存

如果只在两个对象AB产生循环依赖时,可以不需要。

但是如果是ABC产生循环依赖时

@Service
public class TestService1 { 

    @Autowired
    private TestService2 testService2;
    @Autowired
    private TestService3 testService3;

    public void test1() { 
    }
}

@Service
public class TestService2 { 

    @Autowired
    private TestService1 testService1;

    public void test2() { 
    }
}

@Service
public class TestService3 { 

    @Autowired
    private TestService1 testService1;

    public void test3() { 
    }
}

TestService1注入到TestService3又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是ObjectFactory对象。说白了,两次从三级缓存中获取都是ObjectFactory对象,而通过它创建的实例对象每次可能都不一样的。

这样不是有问题?

为了解决这个问题,spring引入的第二级缓存。上面图1其实TestService1对象的实例已经被添加到第二级缓存中了,而在TestService1注入到TestService3时,只用从第二级缓存中获取该对象即可。

是否需要三级缓存

如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择:

  1. 不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
  2. 不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖`的情况下,Bean就可以按着Spring设计原则的步骤来创建。

Spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?
Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

主要参考以下几篇文章做了简单的总结:

一文告诉你Spring是如何利用”三级缓存”巧妙解决Bean的循环依赖问题的

Spring循环依赖三级缓存是否可以去掉第三级缓存

spring:我是如何解决循环依赖的?

Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存

    原文作者:此间少年tq
    原文地址: https://blog.csdn.net/weixin_40133204/article/details/122029228
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞