spring bean的三级缓存原理

当我们在使用 Spring 框架时,通常会遇到循环依赖、AOP 代理等问题。为了解决这些问题,Spring 引入了三级缓存机制,
即 singletonObjects、earlySingletonObjects 和 singletonFactories 三个缓存。本文将详细介绍 Spring 三级缓存的原理和作用。

1. Spring 三级缓存的作用

在 Spring 框架中,Bean 实例化和依赖注入是非常重要的步骤。在这个过程中,循环依赖和 AOP 代理是两个比较常见的问题,而三级缓存机制就是为了解决这些问题而设计的。
具体来说,Spring 的三级缓存的作用如下:

解决循环依赖问题:当一个 Bean 的创建依赖于其他 Bean 的创建时,就可能会出现循环依赖的问题。Spring 的三级缓存机制可以通过代理对象的方式来解决循环依赖问题。

确保单例模式:Spring 默认使用单例模式来管理 Bean,即同一个 Bean 在应用程序的整个生命周期中只被创建一次。而三级缓存机制可以确保单例模式的正确实现。

提高 Spring 的性能:使用缓存可以提高 Spring 的性能,因为缓存可以避免重复创建 Bean 实例的开销。

解决 AOP 代理对象和目标对象名称冲突问题:在使用 AOP 的时候,如果将目标对象和代理对象都缓存在 singletonObjects 缓存中,就可能会出现两个对象名称相同的问题,这可能会导致一些奇怪的问题出现,比如说无法注入正确的对象。因此,Spring 引入了 singletonFactories 缓存来解决这个问题。

2. Spring 三级缓存的实现原理

在 Spring 中,Bean 的创建过程可以分为以下几个阶段:
解析 BeanDefinition:读取配置文件或者注解等方式,将 BeanDefinition 解析成对象。
创建 Bean 实例:根据 BeanDefinition 中的信息,创建 Bean 实例,并进行属性注入和初始化等操作。
将 Bean 实例放入缓存:将创建好的 Bean 实例放入 Spring 的缓存中,以供后续使用。
在这个过程中,Spring 的三级缓存机制就发挥了重要的作用。下面我们来分别介绍三级缓存的作用和实现原理。
2.1 singletonObjects 缓存
singletonObjects 缓存是 Spring 中最常用的一个缓存,用于存储已经创建好的 Bean 实例。这个缓存是一个 ConcurrentHashMap 类型的对象,它将 Bean 的名称作为 key,将 Bean 实例作为 value。
在使用 singletonObjects 缓存时
,在使用 singletonObjects 缓存时,Spring 首先会从缓存中尝试获取 Bean 实例。如果缓存中不存在对应的 Bean 实例,那么就会继续执行创建 Bean 实例的操作。在创建 Bean 实例的过程中,如果发现当前 Bean 已经被创建了,则会从 singletonObjects 缓存中获取该 Bean 实例并返回。
在默认情况下,singletonObjects 缓存的存储策略是“早期曝光”。也就是说,当 Bean 实例被创建后,就会被立即放入 singletonObjects 缓存中。这样可以确保在创建 Bean 实例时就能够获取到该实例的引用,避免了出现循环依赖问题。
2.2 earlySingletonObjects 缓存
earlySingletonObjects 缓存是 Spring 中比较少用到的一个缓存。它的作用是存储“早期曝光”的 Bean 实例,也就是在创建 Bean 实例时尚未完成依赖注入的 Bean 实例。
在创建 Bean 实例的过程中,如果发现该 Bean 依赖于另外一个还未创建完成的 Bean,那么就会将当前 Bean 实例放入 earlySingletonObjects 缓存中。等到该依赖的 Bean 实例创建完成后,Spring 就会将 earlySingletonObjects 缓存中的 Bean 实例进行依赖注入,并将其移动到 singletonObjects 缓存中。
2.3 singletonFactories 缓存
singletonFactories 缓存是 Spring 中专门为解决 AOP 代理对象和目标对象名称冲突问题而设计的缓存。在使用 AOP 的时候,如果将目标对象和代理对象都缓存在 singletonObjects 缓存中,就可能会出现两个对象名称相同的问题,这可能会导致一些奇怪的问题出现,比如说无法注入正确的对象。
为了解决这个问题,Spring 引入了 singletonFactories 缓存。在创建代理对象时,Spring 首先会创建一个 ObjectFactory 对象,并将其放入 singletonFactories 缓存中。等到需要使用代理对象时,Spring 就会调用 ObjectFactory 的 getObject() 方法来创建代理对象,并将其放入 singletonObjects 缓存中。

3. 三级缓存的使用示例

为了更好地理解 Spring 的三级缓存机制,下面我们来看一个简单的示例。
假设我们有一个名为 User 的 Bean,它依赖于另一个名为 Role 的 Bean。此时,Spring 的 Bean 创建过程可以分为以下几个阶段:
解析 User 和 Role 的 BeanDefinition,将其解析为对象。
创建 Role Bean 实例,并放入 singletonObjects 缓存中。
创建 User Bean 实例,发现它依赖于 Role Bean,将 User 实例放入 earlySingletonObjects 缓存中。
创建 Role Bean 实例的代理对象,并将代理对象
放入 singletonObjects 缓存中。
将 Role Bean 实例注入到 User Bean 实例中。
将 User 实例从 earlySingletonObjects 缓存中移动到 singletonObjects 缓存中。
在这个过程中,三级缓存的作用可以概括为:
singletonObjects 缓存用于存储创建完成并已经进行了依赖注入的 Bean 实例。
earlySingletonObjects 缓存用于存储已经创建但尚未进行依赖注入的 Bean 实例。
singletonFactories 缓存用于存储 AOP 代理对象和目标对象名称相同时的代理工厂对象。
三级缓存的使用示例代码如下:

@Component
public class User {
    
    
 
    @Autowired
    private Role role;



    // 省略其他代码
}
}



@Component
public class Role {
    
    



    // 省略其他代码
}
}



@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
    
    



    @Bean
    public Role role() {
    
    
 
        return new Role();
 
    }



    @Bean
    public User user() {
    
    
 
        return new User();
 
    }



    @Bean
    public RoleInterceptor roleInterceptor() {
    
    
 
        return new RoleInterceptor();
 
    }



    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    
    
 
        return new DefaultAdvisorAutoProxyCreator();
 
    }



    @Bean
    public ProxyFactoryBean roleProxy() {
    
    
 
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
 
        proxyFactoryBean.setTarget(role());
 
        proxyFactoryBean.addAdvice(roleInterceptor());
 
        return proxyFactoryBean;
 
    }
}
}



public class RoleInterceptor implements MethodInterceptor {
    
    
 
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
    
    
 
        System.out.println("before");
 
        Object result = invocation.proceed();
 
        System.out.println("after");
 
        return result;
 
    }
}
}



public class Main {
    
    
 
    public static void main(String[] args) {
    
    
 
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
 
        User user = context.getBean(User.class);
 
    }
}
}

在上面的代码中,我们创建了一个 User Bean,它依赖于 Role Bean。我们还创建了一个 RoleInterceptor 来作为 Role Bean 的 AOP 拦截器,以及一个 Role Bean 的代理对象。
在这个例子中,如果我们将三级缓存的某一级去掉,就可能会导致 Bean 创建失败或者出现一些奇怪的问题,比如说:
如果没有 earlySingletonObjects 缓存,就可能会出现循环依赖问题,导致 Bean 创建失败。
如果没有 singletonFactories 缓存,就可能会出现两个对象名称相同的问题,导致注入错误的对象或者无法注入对象。
因此,Spring 的三级缓存机制可以很好地保证 Bean 的创建和依赖注入的正确性,同时也能够有效地避免一些奇怪的问题出现。

猜你喜欢

转载自blog.csdn.net/weixin_43866043/article/details/130222794