当前位置: 首页 > news >正文

周到的网站建站如何进行市场推广

周到的网站建站,如何进行市场推广,网站建设成本核算模板,wordpress如何安装模板文件夹Spring是如何实现有代理对象的循环依赖 代理对象介入后所产生问题阶段一:A 开始创建阶段二:A 注入 B,转而开始创建 B阶段三:B 从缓存中获取 A 的引用阶段四:A 初始化完成,进行代理增强 解决代理对象的循环依…

Spring是如何实现有代理对象的循环依赖

    • 代理对象介入后所产生问题
        • 阶段一:`A` 开始创建
        • 阶段二:`A` 注入 `B`,转而开始创建 `B`
        • 阶段三:`B` 从缓存中获取 `A` 的引用
        • 阶段四:`A` 初始化完成,进行代理增强
    • 解决代理对象的循环依赖问题
      • `ObjectFactory` 接口
      • 三级缓存的引入:`DefaultSingletonBeanRegistry`
      • 提前暴露代理对象:AbstractAutowireCapableBeanFactory
      • 从三级缓存中获取代理对象:`getSingleton()`
      • 代理对象的生成时机
      • 有代理对象的循环依赖全流程
        • 阶段一:Spring 开始创建 Bean `A`
        • 阶段二:`A` 依赖注入 `B`,触发对 `B` 的创建流程
        • 阶段三:`B` 需要注入 `A`,尝试从缓存中获取
      • 问题所在

源码见:mini-spring

在这里插入图片描述

要解决有代理对象的循环依赖问题,首先要明白代理对象介入时候产生循环依赖的原因,这里是以解决了无代理对象的循环依赖为背景进行解释的。

代理对象介入后所产生问题

那我们可以分析一下为什么有了代理对象之后,会产生循环依赖,看一下下面的例子

public class A {  @Autowired  private B b;  public void func() {}  public B getB() {  return b;  }  public void setB(B b) {  this.b = b;  }  
}
public class B {  @Autowired  private A a;  public A getA() {  return a;  }  public void setA(A a) {  this.a = a;  }  
}
阶段一:A 开始创建
  • Spring 创建了原始的 A 实例,还没有完成属性注入;
  • 这个“半成品 A”被放入 二级缓存,准备暴露 early reference;
  • 但注意:此时这个对象还不是代理,只是最原始的 A
阶段二:A 注入 B,转而开始创建 B
  • Spring 去创建 B,为了能让 B 注入依赖,先把 B 的原始实例放入三级缓存;
  • B 也还没初始化完,但它要注入 A,于是 Spring 尝试从缓存中获取 A
阶段三:B 从缓存中获取 A 的引用
  • 这一步就是关键!此时 A 还未完成代理增强;
  • Spring 会从三级缓存中调用 getEarlyBeanReference() 获取 A 的 early reference(理论上可以是代理);
  • 但如果没有设置好,返回的是原始对象;
  • 于是,B 中注入了原始 A 的引用,而不是代理对象
阶段四:A 初始化完成,进行代理增强
  • 初始化流程继续走完,Spring 对 A 执行了 BeanPostProcessor
  • 此时 A 被包装成了代理对象(比如 AProxy);
  • Spring 将 AProxy 注册进一级缓存(singletonObjects),作为最终使用的 Bean。

此时容器中拿到的是代理对象 AProxy,但注入进 B 中的还是早期原始对象 A

  • AProxy ≠ A,逻辑上是同一个业务对象,但代理层包裹的逻辑(如事务、切面)无法被触发;
  • 结果就是:外部调用 AProxy 时能进入切面逻辑,而 B 中注入的 A 却绕过了代理逻辑
  • 这就导致了预期之外的行为,甚至引发潜在错误。

解决代理对象的循环依赖问题

在前文的描述中,我们已经明确了为什么当代理对象介入时,会使循环依赖问题变得复杂。简单来说,问题的根源在于:提前暴露的是原始 Bean 的引用,而不是代理对象的引用。因此,解决该问题的关键在于:在存在代理对象的情况下,如何提前暴露代理对象的引用


ObjectFactory 接口

public interface ObjectFactory<T> {/*** 创建并返回一个泛型类型 T 的对象。* 该方法抽象化了对象的获取过程,允许延迟创建,* 调用者无需关心对象的具体创建细节。** @return 泛型类型 T 的对象实例*/T getObject();
}

在 Spring 中,ObjectFactory<T> 是一个功能性接口,常用于延迟获取对象实例。它的典型使用场景包括:懒加载、循环依赖处理、作用域管理等。通过封装一个 getObject() 方法,Spring 可以在真正需要某个对象时再执行创建逻辑,避免过早初始化。


三级缓存的引入:DefaultSingletonBeanRegistry

// 三级缓存:ObjectFactory 封装的早期引用
protected final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);public void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {singletonFactories.put(beanName, singletonFactory);
}

Spring 通过在 DefaultSingletonBeanRegistry 中引入三级缓存 singletonFactories,来保存 Bean 的“早期引用”(Early Reference)。而这些引用由 ObjectFactory 封装,以支持延迟获取。


提前暴露代理对象:AbstractAutowireCapableBeanFactory

if (beanDefinition.isSingleton()) {Object finalBean = bean;addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, finalBean));
}

在 Bean 实例化完成之后,但尚未进行属性填充之前,Spring 会将其通过 ObjectFactory 封装后加入三级缓存。关键在于此时调用 getEarlyBeanReference(),如果需要创建代理对象,就在这个阶段完成。

private Object getEarlyBeanReference(String beanName, Object bean) {Object exposedObject = bean;for (BeanPostProcessor processor : getBeanPostProcessors()) {if (processor instanceof SmartInstantiationAwareBeanPostProcessor) {exposedObject = ((SmartInstantiationAwareBeanPostProcessor) processor).getEarlyBeanReference(exposedObject, beanName);}}return exposedObject;
}

这段逻辑的核心作用是:如果某个 BeanPostProcessor 支持提前生成代理(如 AOP),就允许它在此阶段返回代理对象


从三级缓存中获取代理对象:getSingleton()

#DefaultSingletonBeanRegistry

@Override
public Object getSingleton(String beanName) {Object singletonObject = singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();earlySingletonObjects.put(beanName, singletonObject);singletonFactories.remove(beanName);}}}return singletonObject;
}

在创建某个 Bean 的过程中,如果发生了依赖注入需求(如 A 注入 B,B 又注入 A),则可以通过 getSingleton() 从三级缓存中获取提前暴露的代理对象,从而避免注入的是原始未增强的对象实例


代理对象的生成时机

#SmartInstantiationAwareBeanPostProcessor

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {Object getEarlyBeanReference(Object bean, String beanName);
}

Spring 中的代理生成主要依赖 SmartInstantiationAwareBeanPostProcessor。其方法 getEarlyBeanReference 允许在 Bean 完整初始化前就返回代理对象。

#AbstractAdvisorAutoProxyCreator

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {earlyProxyReferences.add(beanName);return wrapIfNecessary(bean, beanName); // 判断是否需要创建代理
}@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {if (earlyProxyReferences.contains(beanName)) {return bean;}return wrapIfNecessary(bean, beanName);
}

当一个 Bean 被代理时,如果我们只是将原始对象暴露给其他依赖它的 Bean,就可能导致依赖方拿到的是未增强的实例,进而绕过了 AOP 等代理逻辑。Spring 通过三级缓存 + ObjectFactory + SmartInstantiationAwareBeanPostProcessor,实现了在 Bean 实例化之后但属性注入之前,提前暴露真正的代理对象引用,从而优雅地解决了带有代理对象的循环依赖问题

其实到这里还有一个问题,我们先来思考一下有代理对象的循环依赖全流程

有代理对象的循环依赖全流程

阶段一:Spring 开始创建 Bean A
  • Spring 首先尝试创建 Bean A,执行构造方法,生成一个尚未完成依赖注入的“半成品 A”;

  • 按照三级缓存机制的设计:

    • A 的原始实例会被封装成一个 ObjectFactory 放入三级缓存(singletonFactories)
  • 这一步的关键是:尚未完成依赖注入的 A,已经为可能的循环依赖提前“准备好了引用”。

⚠️ 注意:这时候的 A 并不是代理对象,只是最原始的实例。代理对象的创建通常发生在初始化之后。


阶段二:A 依赖注入 B,触发对 B 的创建流程
  • 接下来,Spring 发现 A 依赖 B,于是进入创建 Bean B 的流程;

  • 同样地,Spring 会为 B 创建一个原始实例,并把 BObjectFactory 提前放入三级缓存;

  • 但此时,B 也依赖 A,于是 Spring 又尝试获取 A 的实例来注入到 B 中。


阶段三:B 需要注入 A,尝试从缓存中获取
  • 这是整个流程的核心关键点!

  • Spring 调用 getSingleton("A") 方法,在一级、二级缓存都找不到 A(因为尚未放入),于是尝试从三级缓存中获取;

  • 调用 ObjectFactory.getObject(),这实际上就是调用 getEarlyBeanReference(beanName, bean) 方法。

如果启用了 AOP(如 @Aspect),这个方法就会在这里提前返回 A 的代理对象(AProxy)

  • 于是,B 拿到的是代理对象 AProxy,注入完成。

  • 这就是 Spring 解决“代理对象循环依赖”问题的关键——通过 getEarlyBeanReference 方法,在依赖注入前就构造代理对象并暴露出来。

✅ 最终,B 中保存的就是完整的 AProxy 引用,而不是原始 A,这就避免了代理失效的问题。

问题所在

在B当中已经创建了A的代理对象,并将其加入到了二级缓存当中,那么回到 A 的创建流程中,Spring 继续执行 A 的依赖注入、初始化等操作

由于之前已经通过 getEarlyBeanReference 创建了代理,postProcessAfterInitialization 阶段会检测到该 Bean 已代理过,不再重复代理,所以在这里我们还需要修改部分逻辑

修改AbstractAutowireCapableBeanFactory的doCreateBean方法

// 创建完毕后加入缓存  
if (beanDefinition.isSingleton()){  // 如果循环依赖创建了代理对象,在这里不会去重复创建需要从缓存当中取出来  Object exposedObject = getSingletonBean(beanName);  super.addSingletonBean(beanName, exposedObject);  
}

最终,Spring 将 AProxy 放入一级缓存,作为 Bean A 的正式实例

此时 B 中的引用是 AProxyA 本身在容器中也是 AProxy引用一致

http://www.hrbkazy.com/news/47392.html

相关文章:

  • 鲜花网站建设项目概述seo搜索引擎优化总结
  • 海阳网网站北京seo排名技术
  • 局域网搭建wordpress网络优化培训骗局
  • 开发施工建设网站审核厦门网络推广培训
  • 西安便宜做网站的郑州关键词优化费用
  • 企业做网站系统推广品牌
  • 国内免费建站网站百度推广
  • 太原这边有做网站的吗如何去推广一个app
  • 郑州高端网站建设多少钱输入搜索内容
  • 全栈网站开发工程师海南百度推广运营中心
  • 网站服务器管理系统深圳百度seo怎么做
  • 工具类网站如何做排名制作网站软件
  • 邯郸网站制作费用好项目推荐平台
  • django做网站和js做网站最新的国际新闻
  • 镇江企业网站建设哪家建设公司网站
  • 做网站 教程下载官方正版百度
  • 国内最大ae模板下载网站seo排名点击软件运营
  • 黑龙江专业建站引擎搜索
  • 一个网站怎么做多条线路新闻式软文经典案例
  • 生物科技网站建设方案杭州百度快照优化公司
  • 互联网行业建设网站合肥关键词排名优化
  • 做土建资料有什么网站没谷歌浏览器 安卓下载2023版官网
  • 8个公开大数据网站广告投放运营主要做什么
  • 哪个网站可以做优惠券专业营销策划团队
  • 营销网站建站百度知道网页版登录入口
  • 罗湖企业网站建设百度搜索引擎排名规则
  • 什么网站能找到做展览的工人深圳外贸seo
  • 网站做系统的靠什么挣钱张雷明任河南省委常委
  • 有些网站打开特别慢seo排名优化app
  • 漳州城乡住房建设部网站青岛网站建设运营推广