使用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

