You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何SpyPostProcessor未效仿AbstractAutoProxyCreator实现getEarlyBeanReference引发循环引用

为什么MockitoPostProcessor#SpyPostProcessor无法复用代理解决循环引用问题

核心原因

AbstractAutoProxyCreator的缓存逻辑

AbstractAutoProxyCreator是Spring AOP体系专门用于代理生成的处理器,内部维护了earlyProxyReferences缓存,缓存键为bean名称。当在getEarlyBeanReference()阶段生成代理后,会将代理对象存入该缓存;到postProcessAfterInitialization()阶段时,会优先查询缓存,如果已经存在对应bean的代理,直接返回缓存实例,不会重复生成,因此两个阶段返回的是同一个代理对象,不会出现循环引用下的包装冲突。

MockitoPostProcessor#SpyPostProcessor的设计差异

  • 无提前代理缓存机制:SpyPostProcessor没有设计对应提前暴露代理的缓存逻辑,无论getEarlyBeanReference()阶段是否生成过spy代理,在postProcessAfterInitialization()阶段都会重新生成一个全新的spy代理实例,不会复用之前的对象,直接导致两个阶段返回的代理不一致。
  • Spy代理生成逻辑特殊:@SpyBean的设计要求spy代理必须基于完全完成属性注入、初始化的成品bean实例生成,才能保证spy的拦截逻辑符合预期(比如正确保留原bean的业务逻辑、正常统计调用次数等)。如果在getEarlyBeanReference()阶段用还未完成初始化的半成品bean生成spy,后续bean初始化完成后,该spy代理的状态是错误的,因此Mockito的设计逻辑里不会提前缓存半成品的spy代理,而是等bean完全初始化后再生成最终的代理对象。
  • 逻辑优先级不兼容:SpyPostProcessor的执行优先级低于Spring自带的AOP代理处理器,且其处理逻辑是直接替换原有bean实例,不会合并Spring AOP的代理缓存逻辑,也不会读取AbstractAutoProxyCreator维护的缓存,因此无法复用AOP体系的代理缓存能力。

异常触发流程

你提供的代码属于典型的循环依赖场景:

@SpringBootTest
public class MockitoTest {
    @SpyBean
    private DevComponent devComponent;
    @Autowired
    private TestComponent testComponent;
    @Test
    public void testMockito() {
        // ...
    }
}

@Component
public class DevComponent {
    @Autowired
    private TestComponent testComponent;
}

@Component
public class TestComponent {
    @Autowired
    private DevComponent devComponent;
}

触发异常的完整流程为:

  1. 初始化devComponent时,因为存在循环依赖,会提前调用getEarlyBeanReference()生成第一个spy代理,注入到testComponent中
  2. 等devComponent完成全量初始化后,postProcessAfterInitialization()生成第二个全新的spy代理
  3. Spring检测到注入到testComponent的代理和最终生成的代理不是同一个实例,抛出如下异常:

创建名称为'devComponent'的Bean时出错:名称为'devComponent'的Bean作为循环引用的一部分,其原始版本已被注入到其他Bean [testComponent] 中,但最终被包装。这意味着上述其他Bean没有使用该Bean的最终版本。这通常是过急的类型匹配导致的——例如,考虑使用关闭'allowEagerInit'标志的'getBeanNamesForType'方法。


常见解决方案

  • 打破循环依赖:调整业务代码拆分依赖,或在注入点添加@Lazy注解实现延迟加载
  • 给@SpyBean对应的注入点也添加@Lazy注解,避免提前触发bean的提前暴露逻辑
  • 若使用Spring Boot 2.6+版本,可临时开启循环依赖支持(仅做临时验证用,不推荐线上使用)

内容的提问来源于stack exchange,提问作者SaberSword

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 05:18:01