spring的三级缓存

  • 一级缓存
    • SingletonObject 存放完全初始化好的bean,该缓存取出来的bean 可以直接使用
  • 二级缓存
    • earlySingletonObject 提前曝光单单例对象的cache,存放原始对象bean(尚未填充属性),用于解决循环依赖。
  • 三级缓存
    • SingletonFacotoies单例对象工厂的cache,存放 ObjectFactory对象,用于解决循环依赖。

三级缓存中 单例对象的加入时机

  1. 先从一级缓存中去获取,
  2. 一级缓存没有则去二级缓存中去取,
  3. 二级缓存中如果没有,则去3级中找。3级缓存没有则创建 ObjectFactory对象。
  4. 如果三级缓存中有则剪切到二级缓存。这样做的目的是,在init()时加载的是同一个对象。
  5. init 之后,则剪切到 一级缓存。

是否可以没有二级缓存

如果是两个对象产生循环依赖,可以不要二级缓存。但是如果是3个就有问题了

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

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

@Service
public class A {
    @Autowired
    private C c;
    @Autowired
    private B b;
    ....
}

@Service
public class B {
    @Autowired
    private A a;
    ....
}

@Service
public class C {
    @Autowired
    private A a;
    ....
}

是否需要三级缓存

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

不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。

不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖`的情况下,Bean就可以按着Spring设计原则的步骤来创建。

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

06-08 01:17