在Spring框架中,三级缓存机制是其解决循环依赖问题的关键设计之一,特别是在处理单例bean的循环依赖时。这三级缓存分别是:
-
beanDefinitionMap:
- 这是第一级缓存,主要用于存储所有Bean的定义信息(
BeanDefinition
对象)。当Spring容器启动并解析配置(如XML配置或注解)时,会将每个Bean的定义加载到这个Map中。BeanDefinition
包含了类名、作用域、依赖关系等元数据信息。
- 这是第一级缓存,主要用于存储所有Bean的定义信息(
-
singletonObjects:
- 第二级缓存,用于存储完全初始化完毕的单例Bean实例。当一个单例Bean完成了实例化和所有初始化过程(包括属性注入、初始化方法调用等),它会被放置到这里。从这个缓存中取出的Bean是完全可用的,可以直接注入到其他需要它的Bean中。
- 第二级缓存,用于存储完全初始化完毕的单例Bean实例。当一个单例Bean完成了实例化和所有初始化过程(包括属性注入、初始化方法调用等),它会被放置到这里。从这个缓存中取出的Bean是完全可用的,可以直接注入到其他需要它的Bean中。
-
singletonFactories
- 这是第三级缓存,存放的是生产Bean实例的工厂对象(
ObjectFactory
)。当一个单例Bean被创建但尚未完成其全部初始化过程时,会先放入这个缓存中。使用工厂对象的好处在于,它可以延后Bean的完整初始化过程,直到真正需要使用Bean时才进行最后的初始化步骤。这对于解决循环依赖至关重要,因为它允许Spring在遇到循环依赖时,能够返回一个可用来创建实例但尚未完全初始化的工厂引用,从而打破了循环,后续再完成剩余的初始化步骤。
- 这是第三级缓存,存放的是生产Bean实例的工厂对象(
三级缓存的作用:
- 解决循环依赖:通过这三级缓存,Spring能够在实例化Bean的过程中,即使遇到循环依赖的情况,也能够确保每个Bean至少有一个可注入的引用,哪怕这个引用还未完全初始化。这样,Spring就可以继续完成依赖链上的其他Bean的创建,最终解决循环依赖问题。
- 提高性能:避免了不必要的重复实例化,特别是对于单例Bean,一旦创建就可以重复利用。
- 支持AOP代理:在处理包含AOP代理的循环依赖时,三级缓存的设计尤为重要,它确保了即使是在AOP代理创建过程中也能正确处理依赖关系,避免了直接使用未代理的对象。
举例说明:
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
处理过程:
BeanDefinitionMap:
Spring启动时,首先读取配置,发现ServiceA和ServiceB的定义,将它们的BeanDefinition分别加入到beanDefinitionMap中。
创建ServiceA:
Spring开始创建ServiceA的实例,此时ServiceA依赖于ServiceB,但ServiceB尚未创建。
因为是单例模式,Spring尝试从singletonObjects缓存中获取ServiceB,但未找到,因为ServiceB还没创建;这时将ServiceA实例(已经创建但未实例化)包装进ObjectFactory【通过SingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法包装】
,将ObjectFactory放入singletonFactories三级缓存中。
创建ServiceB(同时处理循环依赖):
接着Spring转而创建ServiceB实例,ServiceB又依赖于ServiceA。
Spring再次尝试从singletonObjects获取ServiceA,但因为ServiceA的创建尚未完成,它会发现ServiceA存在于singletonFactories缓存中作为一个半成品(即ObjectFactory),从singletonFactories
中获取到代表ServiceA
的ObjectFactory【ObjectFactory#getObject】获取到
ServiceA实例,并注入到ServiceB中,这样就打破了循环依赖的死锁。
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
完成初始化:随着循环依赖被临时解决,Spring继续完成ServiceA
和ServiceB
的初始化,包括完成有依赖注入、调用初始化方法等。最终,当所有步骤完成,两个Bean都会被放入singletonObjects
缓存,作为完全初始化的单例实例。
简而言之:分别创建A和B两个对象的时候,都会创建对应的ObjectFactory对象放在三级缓存中,然后依赖对象的时候,会先从二级缓存中找需要的对象,如果找不到,就会去三级缓存中找到该对象的ObjectFactory,然后通过该ObjectFactory对象中返回需要的A或者B对象;最后将初始化后的对象放入二级缓存中。