spring三级缓存.png

缓存 说明
singletonObjects 第一级缓存,存放可用的成品Bean
earlySingletonObjects 第二级缓存,存放半成品的Bean ,半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。
singletonFactories 第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。

Spring解决循环依赖的核心思想在于提前曝光:
1、通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。2、A对象需要注入B对象,发现缓存里还没有B对象,将半成品对象A放入半成品缓存。3、通过构建函数创建B对象(B对象是半成品,还没注入属性和调用init方法)。4、B对象需要注入A对象,从半成品缓存里取到半成品对象A。5、B对象继续注入其他属性和初始化,之后将完成品B对象放入完成品缓存。6、A对象继续注入属性,从完成品缓存中取到完成品B对象并注入。7、A对象继续注入其他属性和初始化,之后将完成品A对象放入完成品缓存。

一. 在构造Bean对象之后,将对象提前曝光到缓存中,这时候曝光的对象仅仅是构造完成,还没注入属性和初始化。

  1. public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
  2. implements AutowireCapableBeanFactory {
  3. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  4. throws BeanCreationException {
  5. ……
  6. // 是否提前曝光
  7. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  8. isSingletonCurrentlyInCreation(beanName));
  9. if (earlySingletonExposure) {
  10. if (logger.isTraceEnabled()) {
  11. logger.trace("Eagerly caching bean '" + beanName +
  12. "' to allow for resolving potential circular references");
  13. }
  14. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  15. }
  16. ……
  17. }
  18. }

从源码可以得知,doGetBean最初是查询缓存,一二三级缓存全部查询,如果三级缓存存在则将Bean早期引用存放在二级缓存中并移除三级缓存。(升级为二级缓存)

2、提前曝光的对象被放入Map> singletonFactories缓存中,这里并不是直接将Bean放入缓存,而是包装成ObjectFactory对象再放入

  1. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(singletonFactory, "Singleton factory must not be null");
  3. synchronized (this.singletonObjects) {
  4. if (!this.singletonObjects.containsKey(beanName)) {
  5. //一级缓存不存在 放入三级缓存
  6. this.singletonFactories.put(beanName, singletonFactory);
  7. //从二级缓存移除
  8. this.earlySingletonObjects.remove(beanName);
  9. this.registeredSingletons.add(beanName);
  10. }
  11. }
  12. }

3、为什么要包装一层ObjectFactory对象?

如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择:
1、不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
2、不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。
Spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map earlySingletonObjects。

  1. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  2. Object exposedObject = bean;
  3. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  4. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  5. if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
  6. SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
  7. exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
  8. }
  9. }
  10. }
  11. return exposedObject;
  12. }

为了防止对象在后面的初始化(init)时重复代理,在创建代理时,earlyProxyReferences缓存会记录已代理的对象。

  1. private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
  2. @Override
  3. public Object getEarlyBeanReference(Object bean, String beanName) {
  4. Object cacheKey = getCacheKey(bean.getClass(), beanName);
  5. this.earlyProxyReferences.put(cacheKey, bean);
  6. return wrapIfNecessary(bean, beanName, cacheKey);
  7. }

4、注入属性和初始化

提前曝光之后:1、通过populateBean方法注入属性,在注入其他Bean对象时,会先去缓存里取,如果缓存没有,就创建该对象并注入。2、通过initializeBean方法初始化对象,包含创建代理。

  1. public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
  2. implements AutowireCapableBeanFactory {
  3. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  4. throws BeanCreationException {
  5. ……
  6. // Initialize the bean instance.
  7. Object exposedObject = bean;
  8. try {
  9. populateBean(beanName, mbd, instanceWrapper);
  10. exposedObject = initializeBean(beanName, exposedObject, mbd);
  11. }
  12. catch (Throwable ex) {
  13. if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
  14. throw (BeanCreationException) ex;
  15. }
  16. else {
  17. throw new BeanCreationException(
  18. mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
  19. }
  20. }
  21. ……
  22. }
  23. }
  24. // 获取要注入的对象
  25. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  26. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  27. // 一级缓存
  28. Object singletonObject = this.singletonObjects.get(beanName);
  29. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  30. synchronized (this.singletonObjects) {
  31. // 二级缓存
  32. singletonObject = this.earlySingletonObjects.get(beanName);
  33. if (singletonObject == null && allowEarlyReference) {
  34. // 三级缓存
  35. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  36. if (singletonFactory != null) {
  37. singletonObject = singletonFactory.getObject();
  38. this.earlySingletonObjects.put(beanName, singletonObject);
  39. this.singletonFactories.remove(beanName);
  40. }
  41. }
  42. }
  43. }
  44. return singletonObject;
  45. }
  46. }

5. 放入已完成创建的单例缓存

在经历了以下步骤之后,最终通过addSingleton方法将最终生成的可用的Bean放入到单例缓存里。
1、AbstractBeanFactory.doGetBean ->2、DefaultSingletonBeanRegistry.getSingleton ->
3、AbstractAutowireCapableBeanFactory.createBean ->4 AbstractAutowireCapableBeanFactory.doCreateBean ->5、DefaultSingletonBeanRegistry.addSingleton

  1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  2. /** Cache of singleton objects: bean name to bean instance. */
  3. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  4. /** Cache of singleton factories: bean name to ObjectFactory. */
  5. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  6. /** Cache of early singleton objects: bean name to bean instance. */
  7. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  8. protected void addSingleton(String beanName, Object singletonObject) {
  9. synchronized (this.singletonObjects) {
  10. this.singletonObjects.put(beanName, singletonObject);
  11. this.singletonFactories.remove(beanName);
  12. this.earlySingletonObjects.remove(beanName);
  13. this.registeredSingletons.add(beanName);
  14. }
  15. }
  16. }

6.为什么Sping不选择二级缓存方式,而是要额外加一层缓存?

如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。
Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。

如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

这实际上涉及到 AOP,如果创建的 Bean是有代理的,那么注入的就应该是代理 Bean,而不是原始的 Bean。但是 Spring一开始并不知道 Bean是否会有循环依赖,通常情况下(没有循环依赖的情况下),Spring 都会在完成填充属性,并且执行完初始化方法之后再为其创建代理。但是,如果出现了循环依赖的话,Spring 就不得不为其提前创建代理对象,否则注入的就是一个原始对象,而不是代理对象。因此,这里就涉及到应该在哪里提前创建代理对象