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

使用ProxyFactoryBean时如何解决循环依赖场景下的注入失败问题?

问题场景

我有以下存在循环依赖的Bean:

@Component
public class CircleService1 {

  @Resource
  private CircleService2 circleService2;

  public void test() {
  }
}

@Component
public class CircleService2 {

  @Resource
  private CircleService3 circleService3;
}

@Component
public class CircleService3 {

  @Resource
  private CircleService1 circleService1;

}

开启了allow-circular-references确保应用能正常启动,原本运行正常,但当我在BeanPostProcessor的postProcessBeforeInitialization方法中使用ProxyFactoryBean为循环依赖链中的任意类生成代理时,出现注入失败并触发以下错误:

@Override
public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName)
    throws BeansException {
  if (/* satisfied some condition */) {
      ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
      proxyFactory.setTarget(bean);
      proxyFactory.addAdvice(myInterceptor);
      proxyFactory.setBeanFactory(beanFactory);
      proxyFactory.setProxyTargetClass(true);
      return proxyFactory.getObject();
    }

  return bean;
}

错误栈:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circleService1': Bean with name 'circleService1' has been injected into other beans [circleService3] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:649) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.18.jar:5.3.18]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.18.jar:5.3.18]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740) [spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415) [spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) [spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) [spring-boot-2.6.6.jar:2.6.6]

已知使用Aspect或给Bean加@Lazy可以解决,但我正在开发框架,无法要求调用方加@Lazy,且需要继续使用ProxyFactoryBean生成代理,请问如何规避这个错误?


解决方案

1. 替换ProxyFactoryBean为ProxyFactory直接创建代理

ProxyFactoryBean是FactoryBean,在postProcessBeforeInitialization中调用getObject()会触发额外的Bean生命周期逻辑,容易和循环依赖的早期引用冲突。改用ProxyFactory直接创建代理,避免FactoryBean的复杂逻辑:

@Override
public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName)
    throws BeansException {
  if (/* satisfied some condition */) {
      ProxyFactory proxyFactory = new ProxyFactory(bean);
      proxyFactory.addAdvice(myInterceptor);
      proxyFactory.setProxyTargetClass(true);
      // 拦截器不依赖BeanFactory时可省略此设置
      return proxyFactory.getProxy();
    }

  return bean;
}

2. 实现SmartInstantiationAwareBeanPostProcessor,在getEarlyBeanReference中生成代理

Spring处理循环依赖时,会提前调用getEarlyBeanReference方法获取Bean的早期引用。把代理生成逻辑移到这里,确保循环依赖中注入的就是最终的代理对象:

public class MyProxyPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    private final MethodInterceptor myInterceptor;

    public MyProxyPostProcessor(MethodInterceptor myInterceptor) {
        this.myInterceptor = myInterceptor;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        if (/* satisfied some condition */) {
            ProxyFactory proxyFactory = new ProxyFactory(bean);
            proxyFactory.addAdvice(myInterceptor);
            proxyFactory.setProxyTargetClass(true);
            return proxyFactory.getProxy();
        }
        return bean;
    }

    // 仅保留非代理相关的初始化前置处理
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

3. 调整代理生成时机到postProcessAfterInitialization

如果业务允许,把代理生成逻辑移到postProcessAfterInitialization方法中。此时Bean的初始化和循环依赖注入已经完成,不会出现早期原始对象被注入的问题:

@Override
public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName)
    throws BeansException {
  if (/* satisfied some condition */) {
      ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
      proxyFactory.setTarget(bean);
      proxyFactory.addAdvice(myInterceptor);
      proxyFactory.setBeanFactory(beanFactory);
      proxyFactory.setProxyTargetClass(true);
      return proxyFactory.getObject();
    }

  return bean;
}

注意:若拦截器需要在Bean初始化前生效,该方法不适用。

4. 手动替换单例注册表中的早期引用

如果必须在postProcessBeforeInitialization生成代理,可手动操作Spring底层注册表替换早期引用,确保后续注入的是代理对象:

@Override
public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName)
    throws BeansException {
  if (/* satisfied some condition */) {
      ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
      proxyFactory.setTarget(bean);
      proxyFactory.addAdvice(myInterceptor);
      proxyFactory.setBeanFactory(beanFactory);
      proxyFactory.setProxyTargetClass(true);
      Object proxy = proxyFactory.getObject();
      // 替换单例注册表中的早期引用
      ((DefaultSingletonBeanRegistry) beanFactory).registerSingleton(beanName, proxy);
      return proxy;
    }

  return bean;
}

注意:该方式属于底层操作,需谨慎使用,避免破坏Spring生命周期管理。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:04:53