Bean 的创建和管理

简介: Bean 的创建和管理

在 Spring 框架中,Bean 的创建和管理是其核心功能之一。为了优化 Bean 的创建过程并解决循环依赖等问题,Spring 引入了三级缓存机制。让我们深入探讨这一机制的工作原理和实现细节。

 

1. 什么是三级缓存?

 

Spring 的三级缓存主要用于解决单例 Bean 创建时的循环依赖问题。三级缓存分别是:

 

1. **一级缓存(singletonObjects)**:已经完全初始化好的单例 Bean。

2. **二级缓存(earlySingletonObjects)**:提前暴露的单例对象,尚未完全初始化,但已经实例化。

3. **三级缓存(singletonFactories)**:单例工厂,用于创建 Bean 对象的工厂。

 

2. 三级缓存的具体实现

 

三级缓存的实现主要在 `DefaultSingletonBeanRegistry` 类中进行。以下是该类中的相关属性:

 

```java
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    // 一级缓存:存放完全初始化好的单例 Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
    // 二级缓存:提早曝光的单例对象,存放原始的 Bean 实例
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
 
    // 三级缓存:存放单例工厂对象,Bean工厂
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}
```

 

3. 三级缓存的工作流程

 

3.1 Bean 创建前的准备

 

当 Spring 需要创建一个 Bean 时,会先检查三级缓存,看看是否可以复用现有的 Bean 实例。

 

3.2 从缓存中获取 Bean

 

以下是从缓存中获取 Bean 的代码片段:

 

```java
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存中获取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 如果一级缓存中没有,则从二级缓存中获取
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 如果二级缓存中也没有,则从三级缓存中获取,并放入二级缓存中
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}
```

 

3.3 将 Bean 放入缓存

 

当 Bean 被创建出来后,会逐步将其放入各级缓存中,最终放入一级缓存中以表示它已经完全初始化好。

 

```java
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.earlySingletonObjects.remove(beanName);
        this.singletonFactories.remove(beanName);
    }
}
```

 

3.4 处理循环依赖

 

当一个 Bean 在创建过程中依赖另一个尚未完全初始化的 Bean 时,Spring 可以通过三级缓存机制提前暴露 Bean 的引用,从而解决循环依赖问题。

 

例如,当 Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A 时,创建过程如下:

 

1. 创建 Bean A,发现需要依赖 Bean B。

2. 检查缓存,发现 Bean B 尚未初始化,于是去创建 Bean B。

3. 创建 Bean B,发现依赖 Bean A。

4. 检查缓存,发现 Bean A 正在创建过程中,但还没有完全初始化。

5. 将 Bean A 的引用提前暴露,通过三级缓存机制,使得 Bean B 可以访问 Bean A。

6. Bean B 创建完成并放入缓存。

7. 返回继续创建 Bean A,使用已经创建好的 Bean B 完成 Bean A 的初始化。

8. 将完全初始化好的 Bean A 放入一级缓存。

 

4. 总结

 

Spring 的三级缓存机制是为了解决单例 Bean 创建过程中的循环依赖问题而设计的。通过引入三级缓存,Spring 可以提前暴露 Bean 的引用,使得其他 Bean 可以在创建过程中使用这些引用,从而有效地解决了循环依赖问题。

 

三级缓存机制的精妙之处在于它分阶段缓存 Bean 实例,从而确保在任何时刻都能找到合适的 Bean 引用,这不仅提高了 Bean 创建的效率,也增强了 Spring 容器的灵活性和健壮性。理解三级缓存机制对于深入掌握 Spring 框架的运行原理和优化应用程序的性能具有重要意义。

目录
相关文章
|
NoSQL MongoDB 存储
MongoDB 一致性模型设计与实现
本文源自阅读了 MongoDB 于 VLDB 19 上发表的 [Tunable Consistency in MongoDB](http://www.vldb.org/pvldb/vol12/p2071-schultz.pdf) 论文之后,在内部所做的分享(分享 PPT 见文末)。现在把分享的内容整理成此文,并且补充了部分在之前的分享中略过的细节,以及在分享中没有提及的 MongoDB Causa
2018 0
MongoDB 一致性模型设计与实现
|
11月前
|
机器学习/深度学习 算法 语音技术
超越传统模型:探讨门控循环单元(GRU)在语音识别领域的最新进展与挑战
【10月更文挑战第7天】随着人工智能技术的不断进步,语音识别已经从一个相对小众的研究领域发展成为日常生活中的常见技术。无论是智能手机上的语音助手,还是智能家居设备,甚至是自动字幕生成系统,都离不开高质量的语音识别技术的支持。在众多用于语音识别的技术中,基于深度学习的方法尤其是递归神经网络(RNNs)及其变体如长短期记忆网络(LSTMs)和门控循环单元(GRUs)已经成为了研究和应用的热点。
496 2
|
存储 安全 API
权限设计种类【RBAC、ABAC】
权限设计种类【RBAC、ABAC】
1534 2
|
11月前
|
关系型数据库 MySQL
用dbeaver创建一个enum类型,并讲述一部分,mysql的enum类型的知识
这篇文章介绍了如何在DBeaver中创建MySQL表的枚举(ENUM)字段,并探讨了MySQL中ENUM类型的一些行为特点,例如ENUM值的默认排序和在插入重复值时的表现。
271 1
用dbeaver创建一个enum类型,并讲述一部分,mysql的enum类型的知识
|
算法 NoSQL Java
spring cloud的限流算法有哪些?
【8月更文挑战第18天】spring cloud的限流算法有哪些?
269 3
|
存储 关系型数据库 MySQL
探讨MySQL什么情况下触发表锁,行锁
MySQL是一种流行的关系型数据库管理系统,它支持多种存储引擎,例如MyISAM和InnoDB。在并发访问数据库的环境下,为了保证数据的完整性和一致性,MySQL会使用锁机制来控制对数据的访问。MySQL中的锁分为表级锁和行级锁,它们在不同的情况下会被触发。
1520 0
|
消息中间件 安全 Java
Java中的线程间通信详解
Java中的线程间通信详解
|
存储 关系型数据库 MySQL
什么是覆盖索引?
本章主要讲解了索引覆盖和回表的相关知识
271 0
|
弹性计算 JavaScript Ubuntu
ECS 挂载 OSS 多Bucket
ECS 挂载 OSS 多Bucket
202 0
|
SQL 中间件 Java
springcloud+eureka整合分布式事务中间件seata
springcloud+eureka整合分布式事务中间件seata
342 92
springcloud+eureka整合分布式事务中间件seata