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一起使用,它导致为依赖项创建延迟分辨率的代理。

##代码##