Spring三级缓存

发布时间:2024年01月18日

Spring框架的依赖注入(DI)是其核心功能之一,为了解决循环依赖的问题,Spring提供了所谓的“三级缓存”。这个机制确保了即使是彼此依赖的bean也能被正确地创建和管理。

Spring循环依赖

循环依赖是指两个或多个bean相互依赖,形成一个闭环。例如,A bean依赖B bean,而B bean同时依赖A bean。在单例模式下,Spring容器通过三级缓存来解决这一问题。

Spring三级缓存简介

Spring容器使用以下三个缓存来存储bean:

  1. 一级缓存(Singleton Objects):
    存储完全初始化好的bean,即依赖注入完全完成,可以直接使用的bean。

  2. 二级缓存(Early Singleton Objects):
    存储原始的bean(尚未完成依赖注入),通常是在bean实例化之后,但在依赖注入开始之前。

  3. 三级缓存(Singleton Factories):
    存储bean工厂对象,用于生成对应的bean,通常会包含一个引用到ObjectFactory,通过这个工厂对象可以获取早期的bean引用。

循环依赖的处理

当容器实例化一个bean时,它会经历以下步骤:

  1. 创建bean实例:
    通过构造器或工厂方法创建bean实例。

  2. 填充属性:
    注入属性值和其他bean的引用。

  3. Bean后处理器:
    进行BeanPostProcessor处理,比如@Autowired注解的处理。

处理循环依赖的关键在于第三步,使用BeanPostProcessor时,如果发现依赖的bean尚未创建,则会通过三级缓存来提早曝光一个bean的引用。

源码解析

在Spring的DefaultSingletonBeanRegistry类中,你会看到如下的三个Map,作为缓存的数据结构:

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

当通过getBean方法请求一个bean时,Spring容器会尝试从一级缓存获取。如果没有找到,它会尝试通过工厂对象(三级缓存)创建一个bean。创建过程中如果需要依赖注入,而所依赖的bean也在创建过程中,就会形成一个循环依赖。

在创建bean的过程中,一旦实例化后,Spring会将其包装成一个工厂对象存入三级缓存,然后继续bean的创建。如果另一个bean需要依赖当前创建中的bean,就可以通过三级缓存的工厂获取到早期引用。

代码示例

这里以一个简化的代码示例来说明Spring如何处理循环依赖:

public class CircularDependencyDemo {

    @Component
    public static class BeanA {
        @Autowired
        private BeanB beanB;
    }

    @Component
    public static class BeanB {
        @Autowired
        private BeanA beanA;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CircularDependencyDemo.class);
        BeanA beanA = context.getBean(BeanA.class);
        BeanB beanB = context.getBean(BeanB.class);
    }
}

在上面的例子中,BeanABeanB形成了循环依赖。Spring容器会通过三级缓存确保两个bean都能被创建。

Spring三级缓存的实现细节

关键的源码在AbstractAutowireCapableBeanFactorydoCreateBean方法中。这个方法会在bean实例化之后、依赖注入之前将其包装为ObjectFactory,并放入三级缓存中。然后在依赖注入的过程中,如果需要引用其他bean,容器会首先检查一级缓存,然后是二、三级缓存,以解决循环依赖。如果当前bean的依赖存在于缓存中,就会使用缓存中的对象来完成注入。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean...
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
    // Allow post-processors to modify the merged bean definition...
    // Initialize the bean instance...
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (...) {
        // Handle initialization failure...
    }
    return exposedObject;
}

populateBean方法中,Spring会处理依赖注入(例如通过@Autowired注解)。如果在这个过程中需要依赖其他bean,Spring就会尝试从缓存中加载这个bean,以满足当前bean的依赖。

需要注意的是,正是这种三级缓存的设计让Spring能够解决单例bean的循环依赖问题。不过,对于原型(Prototype)作用域的bean,Spring无法解决循环依赖问题,因为每次请求原型bean都会创建一个新的实例。

文章来源:https://blog.csdn.net/qq_43012298/article/details/135656779
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。