【发布时间】:2019-09-04 16:35:01
【问题描述】:
我对 Spring 循环依赖项有一个我不理解的特定问题。我已经编译了一个简单的示例来重现该问题并类似于我的大型应用程序:
@Configuration
@ImportResource("classpath:my-spring-config.xml")
public class DeleteMeSpringTest {
@Test
public void test() {
AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext("extremeSpecial");
appContext.register(DeleteMeSpringTest.class);
MainUIFrame master = appContext.getBean(MainUIFrame.class);
System.out.println(master.toString());
}
public static class DialogProvider {
private JComponent component;
public void setComponent(final JComponent master) {
this.component = master;
}
}
@Component(value = "nestedUIComponent")
public static class SomeNestedUIView extends JComponent {
private DialogProvider dialogProvider;
@Autowired
public void setDialogProvider(final @Qualifier("nestedDialogProvider") DialogProvider dialogProvider) {
this.dialogProvider = dialogProvider;
}
}
@Component(value = "mainUIFrame")
public static class MainUIFrame extends JComponent {
private final SomeNestedUIView compA;
private final DialogProvider mainDialogProvider;
public MainUIFrame(final SomeNestedUIView compA, final @Qualifier("mainDialogProvider") DialogProvider dialogProvider) {
this.compA = compA;
this.mainDialogProvider = dialogProvider;
}
}
}
以下 XML bean 定义也存在:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="extremeSpecial.DeleteMeSpringTest$DialogProvider" id="mainDialogProvider">
<property name="component" ref="mainUIFrame"/>
</bean>
<bean class="extremeSpecial.DeleteMeSpringTest$DialogProvider" id="nestedDialogProvider">
<property name="component" ref="nestedUIComponent"/>
</bean>
</beans>
查看代码应该实现以下循环结构:mainDialogProvider -> MainUIFrame -> mainDialogProvider。但是我不明白为什么 Spring 无法解决循环依赖:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainUIFrame': Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainDialogProvider' defined in class path resource [my-spring-config.xml]: Cannot resolve reference to bean 'mainUIFrame' while setting bean property 'component'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mainUIFrame': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1267)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1124)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:99)
at extremeSpecial.DeleteMeSpringTest.test(DeleteMeSpringTest.java:25)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainDialogProvider' defined in class path resource [my-spring-config.xml]: Cannot resolve reference to bean 'mainUIFrame' while setting bean property 'component'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mainUIFrame': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1602)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1354)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:818)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:724)
... 36 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mainUIFrame': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
... 50 more
我原以为通过使用 setter 注入(或 XML 中的属性),Spring 应该能够首先在没有属性的情况下创建 bean,然后再使用 setter 注入循环依赖项。对于更简单的示例(例如,此处演示:https://stackoverflow.com/a/49188520/606513),它的工作原理正是如此,但是我的示例中的某些东西会导致它中断,我需要找出那是什么!
请注意,通过避免使用 XML 文件(并且不需要两个不同的 DialogProvider 实例),Spring 可以毫无问题地解决主循环!
【问题讨论】:
-
异常消息可以提供帮助。
-
@davidxxx 我添加了异常
-
对不起,我以为你真的错过了这里的循环依赖,但你希望解决它,等等让我试试看:-)
-
你用的是什么Spring版本?
-
@MartinvanWingerden 在生产系统 spring-boot 2.0.4 中,对于这个测试,spring-boot 2.0.4 引入的依赖项相同,但只是直接使用 ApplicationContext 而不使用 spring-boot -> spring-context 5.0.8