Spring的循环依赖
时间:2020-01-09 10:44:28 来源:igfitidea点击:
在这篇文章中,我们将看到什么是Spring中的循环依赖以及如何解决循环依赖。
Spring的循环依赖
如果我们主要使用构造函数依赖项注入,则可能会创建无法解决的循环依赖项方案。例如;类A通过构造函数注入需要类B的实例,而类B通过构造函数注入需要类A的实例。在将A类和B类的bean相互注入的这种配置中,Spring IoC容器无法确定应该首先创建哪个bean,并且在检测到此循环引用时在运行时抛出BeanCurrentlyInCreationException。
循环依赖的Spring示例
这是显示在应用程序中循环依赖可能如何发生的示例。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ClassA { private ClassB b; @Autowired ClassA(ClassB b){ this.b = b; } }
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ClassB { private ClassA a; @Autowired ClassB(ClassA a){ this.a = a; } }
有两个类ClassA和ClassB,ClassA依赖于ClassB,ClassB依赖于ClassA,这些依赖项作为构造函数依赖项注入。
遵循Spring XML配置来设置组件扫描。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.theitroad.springproject.service"/> </beans>
我们可以将以下类与main方法一起使用以读取配置并运行示例。
import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main( String[] args ){ // create context using configuration ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml"); } }
在运行示例程序时,由于循环引用,我们将获得类似于以下给出的异常。
11:08:12.031 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classB' 11:08:12.039 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classA' Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'classA' defined in file [F:\theitroad\Spring WorkSpace\SpringProject\target\classes\com\theitroad\springproject\service\ClassA.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'classB' defined in file [F:\theitroad\Spring WorkSpace\SpringProject\target\classes\com\theitroad\springproject\service\ClassB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean@Component public class ClassA { private ClassB b; @Autowired public void setB(ClassB b) { this.b = b; } public void display() { System.out.println("In method of classA"); } }(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85) at com.theitroad.springproject.App.main(App.java:10) Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'classB' defined in file [F:\theitroad\Spring WorkSpace\SpringProject\target\classes\com\theitroad\springproject\service\ClassB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean@Component public class ClassB { private ClassA a; @Autowired public void setA(ClassA a) { this.a = a; } public void display() { System.out.println("In method of classB"); } }(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760) ... 15 more
解决循环依赖
在许多情况下,通过重新设计类,可以避免循环依赖。如果必须具有此类对象依赖关系,则在Spring中使用以避免循环依赖关系,可以使用以下解决方案之一。
1.使用setter注入而不是构造函数注入。使用setter注入时,仅当在bean创建时不使用依赖项时才注入依赖项。
public class App { public static void main( String[] args ){ // create context using configuration ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml"); ClassA a = context.getBean("classA", ClassA.class); a.display(); context.close(); } }
:24:06.884 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classA' 11:24:07.019 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'classB' 11:24:07.038 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'employeeService' In method of classA
如我们所见,这些依赖项将作为setter依赖项注入。
在运行应用程序时
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component public class ClassA { private ClassB b; @Autowired @Lazy ClassA(ClassB b){ this.b = b; } public void display() { System.out.println("In method of classA"); } }
输出:
##代码##2.在Spring中避免循环依赖的另一种方法是延迟初始化一个bean。这样,即使使用构造函数注入,Bean也仅在需要时才完全初始化。
这是更新的classA,其中@Lazy也与@Autowired一起使用,它导致为依赖项创建延迟分辨率的代理。
##代码##