春季循环依赖关系,即使使用通过设置器进行注入

时间:2019-09-04 16:35:01

标签: java spring

我对我不了解的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可以解决主循环,而不会出现问题!

1 个答案:

答案 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

编辑:我的解决方案不会忽略存在两个不同实例的事实:

enter image description here

相关问题