我对我不了解的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可以解决主循环,而不会出现问题!
答案 0 :(得分:1)
我在最新的Spring Boot中测试了您的代码,并且工作正常,我所做的唯一更改是用@Bean
替换了xml配置并移动了setter,而xml-beans实际上是自动构造的,因此导致了问题:
@Bean
public MainFrameDialogProvider mainDialogProvider(MainUIFrame master) {
return new MainFrameDialogProvider(master, "mainDialogProvider");
}
@Bean
public MainFrameDialogProvider nestedDialogProvider(SomeNestedUIView master) {
return new MainFrameDialogProvider(master, "nestedDialogProvider");
}
public static class MainFrameDialogProvider {
private final Object master;
private final String nestedDialogProvider;
public MainFrameDialogProvider(Object master, String nestedDialogProvider) {
this.master = master;
this.nestedDialogProvider = nestedDialogProvider;
}
}
@Component
public static class SomeNestedUIView {
private MainFrameDialogProvider dialogProvider;
@Autowired
public void setMainFrameDialogProvider(@Qualifier("nestedDialogProvider") MainFrameDialogProvider dialogProvider) {
this.dialogProvider = dialogProvider;
}
}
@Component
public static class MainUIFrame {
private SomeNestedUIView compA;
private MainFrameDialogProvider mainFrameDialogProvider;
@Autowired
public void setCompA(SomeNestedUIView compA) {
this.compA = compA;
}
@Autowired
public void setMainFrameDialogProvider(@Qualifier("mainDialogProvider") MainFrameDialogProvider mainFrameDialogProvider) {
this.mainFrameDialogProvider = mainFrameDialogProvider;
}
}
}
使用了spring-boot 2.1.7
编辑:我的解决方案不会忽略存在两个不同实例的事实: