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