https://www.logicbig.com/tutorials/spring-framework/spring-core/circular-dependencies.html
Spring - Circular Dependencies[Updated: Mar 25, 2017, Created: Mar 24, 2017] |
| ||||
Circular dependencies is the scenario when two or more beans try to inject each other via constructor. Let's consider following two classes (outside of Spring framework): public class A { public A(B b){ .... } } public class B { public B(A b){ .... } } It's not possible to write a compilable code which can initialize exactly one instance of each A and B and pass to each other's constructor. We can, however, refactor our above code and can pass circular references via setters but that would kill the semantics of 'initializing mandatory final fields via constructors. The same problem Spring faces when it has to inject the circular dependencies via constructor. Spring throws Example of circular dependencies in Spring@ComponentScan(basePackageClasses = ExampleMain.class, useDefaultFilters = false, //scan only the nested beans of this class includeFilters = {@ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, value = {ExampleMain.BeanB.class, ExampleMain.BeanA.class})}) @Configuration public class ExampleMain { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(ExampleMain.class); } @Component public static class BeanA { private final BeanB beanB; public BeanA(BeanB b) { this.beanB = b; } } @Component public static class BeanB { private final BeanA beanA; public BeanB(BeanA beanA) { this.beanA = beanA; } } } Output Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'exampleMain.BeanA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) ... 33 more Fixing circular dependencies by using setter injection@ComponentScan(basePackageClasses = ExampleMainFix1.class, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, value = {ExampleMainFix1.BeanB.class, ExampleMainFix1.BeanA.class})}) @Configuration public class ExampleMainFix1 { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext( ExampleMainFix1.class); BeanA beanA = context.getBean(BeanA.class); beanA.doSomething(); } @Component static class BeanA { private BeanB beanB; public BeanA() { } public void setB(BeanB b) { this.beanB = b; } public void doSomething() { System.out.println("doing something"); } } @Component static class BeanB { private BeanA beanA; public BeanB() { } public void setBeanA(BeanA beanA) { this.beanA = beanA; } } } Output doing something Fixing circular dependencies by using @Lazy at constructor injection pointUsing @Lazy at injection point is more suitable solution. That way we can keep our final mandatory field as it is. @ComponentScan(basePackageClasses = ExampleMainFix2.class, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, value = {ExampleMainFix2.BeanB.class, ExampleMainFix2.BeanA.class})}) @Configuration public class ExampleMainFix2 { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext( ExampleMainFix2.class); BeanA beanA = context.getBean(BeanA.class); beanA.doSomething(); } @Component static class BeanA { private final BeanB beanB; BeanA(@Lazy BeanB b) { this.beanB = b; } public void doSomething() { beanB.doSomething(); } } @Component static class BeanB { private final BeanA beanA; BeanB(BeanA beanA) { this.beanA = beanA; } public void doSomething() { System.out.println("doing something"); } } } Output doing something Spring uses a proxy instead of the real object at the injection point. This proxy delays the initialization of the underlying object until it is first used. Example ProjectDependencies and Technologies Used:
| Advertisement blocked! Java 11 TutorialsJava 10 TutorialsJava 9 TutorialsRecent Tutorials
| ||||
package com.logicbig.example; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; import org.springframework.stereotype.Component; @ComponentScan(basePackageClasses = ExampleMainFix2.class, useDefaultFilters = false, includeFilters = {@ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, value = {ExampleMainFix2.BeanB.class, ExampleMainFix2.BeanA.class})}) @Configuration public class ExampleMainFix2 { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext( ExampleMainFix2.class); BeanA beanA = context.getBean(BeanA.class); beanA.doSomething(); } @Component static class BeanA { private final BeanB beanB; BeanA(@Lazy BeanB b) { this.beanB = b; } public void doSomething() { beanB.doSomething(); } } @Component static class BeanB { private final BeanA beanA; BeanB(BeanA beanA) { this.beanA = beanA; } public void doSomething() { System.out.println("doing something"); } } } |