spring源码(10)从缓存中获取到单例bean的后续操作(对FactoryBean的处理)

本文详细解析了Spring框架中Bean的实例化过程,重点介绍了如何处理FactoryBean与普通Bean的区别,以及getObjectForBeanInstance方法的具体实现流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述

从缓存中获取到实例,doGetBean第一步就是对我们输入的name做了处理。

  1. 去除FactoryBean的修饰符,也就是name=”&a”,最终处理为name=”a”
  2. 取指定别名最终所指向的beanName

在从缓存中获取实例时,getSingleton(beanName),参数是已经处理的。我们知道如果想要获取FactoryBean getObject方法对应的实例,那么name需要加前缀&。所以通过getSingletom获取到的可能是FactoryBean实例。

getObjectForBeanInstance就是对以上的纠正。

总体流程图如下:
这里写图片描述

protected Object getObjectForBeanInstance(
                    Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

             //如果name的以&为开头,但是beanInstance又不是FactoryBean类型,则抛出BeanIsNotAFactoryException
             if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
                    throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
             }

             //如果beanInstance不是FactoryBean类型,或者不是以&开头,则直接返回实例
             if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
                    return beanInstance;
             }

             //以下步骤处理以&开头的name,即FactoryBean
             Object object = null;
             if (mbd == null) {
                    //尝试从缓存中加载bean
                    object = getCachedObjectForFactoryBean(beanName);
             }
             if (object == null) {
                    //到这里已经明确知道beanInstance为FactoryBean类型
                    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
                    //将存储XML配置文件的GernericBeanDefinition转换成RootBeanDefinition
                    if (mbd == null && containsBeanDefinition(beanName)) {
                          mbd = getMergedLocalBeanDefinition(beanName);
                    }
                    boolean synthetic = (mbd != null && mbd.isSynthetic());

                    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
             }
             return object;
       }
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
             //如果factoryBean为单例子,并且已经在缓存中存在
             if (factory.isSingleton() && containsSingleton(beanName)) {
                    //同步访问singletonObjects
                    synchronized (getSingletonMutex()) {
                          //从缓存中获取bean实例,如果存在直接返回
                          Object object = this.factoryBeanObjectCache.get(beanName);
                          if (object == null) {
                                 object = doGetObjectFromFactoryBean(factory, beanName);
                                 // Only post-process and store if not put there already during getObject() call above
                                 // (e.g. because of circular reference processing triggered by custom getBean calls)
                                 Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                                 if (alreadyThere != null) {
                                       object = alreadyThere;
                                 }
                                 else {
                                       if (object != null && shouldPostProcess) {
                                              try {
                                                    //后续处理
                                                    object = postProcessObjectFromFactoryBean(object, beanName);
                                              }
                                              catch (Throwable ex) {
                                                    throw new BeanCreationException(beanName,
                                                                  "Post-processing of FactoryBean's singleton object failed", ex);
                                              }
                                       }
                                       //加入缓存
                                        this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
                                 }
                          }
                          return (object != NULL_OBJECT ? object : null);
                    }
             }
             else {//如果不是单例
                    Object object = doGetObjectFromFactoryBean(factory, beanName);
                    if (object != null && shouldPostProcess) {
                          try {
                                 //后续处理
                                 object = postProcessObjectFromFactoryBean(object, beanName);
                          }
                          catch (Throwable ex) {
                                 throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
                          }
                    }
                    return object;
             }
       }
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
                    throws BeanCreationException {

             Object object;
             try {
                    if (System.getSecurityManager() != null) {
                          AccessControlContext acc = getAccessControlContext();
                          try {
                                 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                                       @Override
                                       public Object run() throws Exception {
                                                    return factory.getObject();
                                              }
                                       }, acc);
                          }
                          catch (PrivilegedActionException pae) {
                                 throw pae.getException();
                          }
                    }
                    else {
                          //直接调用factorybean的getObject方法
                          object = factory.getObject();
                    }
             }
             catch (FactoryBeanNotInitializedException ex) {
                    throw new BeanCurrentlyInCreationException(beanName, ex.toString());
             }
             catch (Throwable ex) {
                    throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
             }

             // Do not accept a null value for a FactoryBean that's not fully
             // initialized yet: Many FactoryBeans just return null then.
             if (object == null && isSingletonCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(
                                 beanName, "FactoryBean which is currently in creation returned null from getObject");
             }
             return object;
       }
<think>我们正在讨论Java中的三级缓存,特别是在Spring框架中。三级缓存Spring解决循环依赖的关键机制。根据引用[1][3],三级缓存分为三个层次: 1. 第一级缓存(singletonObjects):存放完全初始化好的Bean。 2. 第二级缓存(earlySingletonObjects):存放早期暴露的Bean(尚未填充属性)。 3. 第三级缓存(singletonFactories):存放Bean工厂,用于生成早期Bean(通过ObjectFactory)。 解决循环依赖的过程: 假设A依赖B,B依赖A。 1. 创建A实,并将A的ObjectFactory放入三级缓存(singletonFactories)。 2. A填充属性时发现需要B,于是开始创建B。 3. 创建B实,并将B的ObjectFactory放入三级缓存。 4. B填充属性时发现需要A,于是从三级缓存获取A的ObjectFactory,调用getObject()得到A的早期引用(此时A还未完成属性填充),将A放入二级缓存,同时从三级缓存移除。 5. B完成初始化,放入一级缓存。 6. A得到B的引用,继续完成属性填充和初始化,然后放入一级缓存,同时清除二级缓存和三级缓存中的A。 注意:三级缓存主要解决的是Bean的循环依赖问题,对于原型Bean(prototype)的循环依赖,Spring无法解决,会抛出异常。 另外,根据引用[1][3],三级缓存的使用场景和注意事项: - 避免在构造器注入中产生循环依赖,因为构造器注入在实化之前就需要依赖,此时三级缓存还没有Bean的工厂,无法解决。 - 避免在@PostConstruct方法中调用循环依赖的Bean,因为此时Bean可能还未完全初始化。 除了Spring框架,三级缓存的概念在其他地方也有应用,比如图片加载库(引用[2]): 1. 内存缓存(LruCache):相当于一级缓存。 2. 磁盘缓存(DiskLruCache):相当于二级缓存。 3. 网络加载:相当于三级缓存(但通常不称为缓存,而是数据源)。 在图片加载中,三级缓存的工作流程: 1. 首先从内存缓存中查找图片,找到则返回。 2. 内存缓存没有,则从磁盘缓存中查找,找到则加载到内存并返回。 3. 磁盘缓存没有,则从网络加载,加载完成后存入内存和磁盘缓存。 因此,我们可以将问题分为两部分:Spring框架中的三级缓存和图片加载中的三级缓存。由于用户问题明确提到Java,且引用中主要涉及Spring,因此重点在Spring的三级缓存。 下面,我们将详细解释Spring三级缓存的实现原理,并给出相关问题。</think>### Java 三级缓存实现原理及应用详解 #### 一、三级缓存的核心原理 在 Spring 框架中,三级缓存是解决**循环依赖**的核心机制,通过三个层级的缓存容器协同工作: 1. **第一级缓存(singletonObjects)** 存放**完全初始化**的 Bean,数据结构为 `ConcurrentHashMap`。 公式:$$ \text{singletonObjects} = \{ \text{beanName} \rightarrow \text{成熟 Bean} \} $$ 2. **第二级缓存(earlySingletonObjects)** 存放**半成品 Bean**(已实化但未完成属性注入),数据结构为 `HashMap`。 公式:$$ \text{earlySingletonObjects} = \{ \text{beanName} \rightarrow \text{早期 Bean 引用} \} $$ 3. **第三级缓存(singletonFactories)** 存放**Bean 工厂对象**(`ObjectFactory`),用于生成 Bean 的代理对象,数据结构为 `HashMap`。 公式:$$ \text{singletonFactories} = \{ \text{beanName} \rightarrow \lambda(\text{创建代理}) \} $$ #### 二、工作流程(以 A→B→A 循环依赖为) ```mermaid graph TD A[创建A实] --> B[将A的ObjectFactory放入三级缓存] B --> C[A填充属性时发现依赖B] C --> D[创建B实] D --> E[将B的ObjectFactory放入三级缓存] E --> F[B填充属性时发现依赖A] F --> G{从三级缓存获取A的ObjectFactory} G --> H[执行getObject生成A的代理对象] H --> I[将代理对象存入二级缓存] I --> J[删除三级缓存中的A] J --> K[B完成初始化] K --> L[A完成初始化] L --> M[清除二级缓存] ``` 关键步骤解析: 1. 当 B 需要注入 A 时,通过三级缓存获取 A 的 `ObjectFactory`,生成**代理对象**(解决 AOP 代理问题) 2. 生成的代理对象临时存于二级缓存,避免重复创建代理 3. A 完成初始化后,**升级**到一级缓存,清除下级缓存[^1][^3] #### 三、应用场景与注意事项 **典型应用场景**: 1. 解决构造器注入的循环依赖 2. 支持 AOP 代理对象的循环依赖 3. 优化 Bean 初始化性能(避免重复创建) **注意事项**: 1. **仅适用于 Bean**:原型(prototype)作用域的 Bean 无法使用三级缓存[^3] 2. **避免构造器循环依赖**:若循环依赖发生在构造器阶段,三级缓存无法解决(因实未创建) ```java // 无法解决的场景 @Component public class A { private final B b; public A(B b) { this.b = b; } // 构造器依赖 } ``` 3. **FactoryBean 特殊处理**:需通过 `&` 前缀区分 BeanFactoryBean 本身 4. **并发安全问题**:使用 `synchronized` 保证缓存操作原子性[^1] #### 四、与其他缓存的对比 | **缓存类型** | **应用框架** | 核心目的 | 数据生命周期 | |--------------------|---------------|-------------------------|-------------------| | Spring 三级缓存 | Spring IoC | 解决循环依赖 | Bean 初始化过程 | | MyBatis 二级缓存 | MyBatis | SQL 结果缓存 | Session 关闭前有效 | | 图片加载三级缓存 | Android | 图片资源高效加载 | 内存/磁盘/网络层级 | > 提示:MyBatis 二级缓存可通过 Redis 实现分布式扩展(引用[4]),而图片缓存通常用 `LruCache` + `DiskLruCache` 实现(引用[2])。 #### 五、性能优化建议 1. **减少 Bean 的依赖层次**:深度依赖链会增加缓存查找开销 2. **懒加载非必要依赖**:使用 `@Lazy` 注解延迟初始化 3. **避免过度 AOP 代理**:代理对象创建会触发三级缓存工厂 4. **缓存命中监控**:通过 `BeanFactory` 监控缓存命中率 --- ### 相关问题 1. Spring 三级缓存如何处理 AOP 代理对象的循环依赖? 2. 为什么原型(prototype)作用域的 Bean 无法使用三级缓存解决循环依赖? 3. 如何通过扩展 `ObjectFactory` 实现自定义的三级缓存逻辑? 4. 在分布式环境中,Spring 三级缓存机制可能遇到哪些问题?如何解决? 5. 对比 Spring 三级缓存与 MyBatis 二级缓存在设计思想上的异同点? [^1]: 深度解析 Spring 源码:三级缓存机制探究 [^2]: Android 三级缓存原理及实现 [^3]: Java 八股 - Spring 三级缓存,自动装配原理 [^4]: Mybatis 二级缓存的介绍及使用 [^5]: 缓存一致性协议 MESI 原理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值