Spring的循环依赖性

时间:2020-02-23 14:33:59  来源:igfitidea点击:

如果Spring存在循环依赖性,会发生什么。

问题

Spring有循环依赖性怎么办?
例如:A类需要B类,B类的实例需要A类实例。

让我们创建一个例子。

  1. 创建一个简单的Java Maven项目。
  2. Maven依赖性将Spring和Cglib Maven依赖于Pom.xml。
<dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
  </dependency>
<!-- cglib required for @configuration annotation -->
   <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.0</version>
        </dependency>
 </dependencies>
 <properties>
  <spring.version>4.2.1.RELEASE</spring.version>
 </properties>

所以你的pom.xml看起来像:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"<
  <modelVersion<4.0.0</modelVersion>
  <groupId>org.arpit.theitroad</groupId>
  <artifactId>SpringHelloWorldJavaBasedConfiguration</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>SpringHelloWorldJavaBasedConfiguration</name>
  <description>It provides hello world program for java based config
</description>
 
 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
  </dependency>
<!-- cglib required for @configuration annotation -->
   <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.0</version>
    </dependency>
 </dependencies>
 <properties>
  <spring.version>4.2.1.RELEASE</spring.version>
 </properties>
</project>

3.创建bean类

在package org.arpit.theitroad中创建名为a.java的bean类。

package org.arpit.theitroad;
 
public class A {
 
	B b;
 
	public A(B b) {
		super();
		this.b = b;
	}
	
	public B getB() {
		return b;
	}
	public void setB(B b) {
		this.b = b;
	}
	
	public void doSomeThing()
	{
		System.out.println("Doing some work");
	}
}

创建一个名为b.java的另一个bean类org.arpit.theitroad。
此类将具有高于a.java的依赖。

package org.arpit.theitroad;
 
public class B {
	A a;
 
	public B(A a) {
		super();
		this.a = a;
	}
 
	public A getA() {
		return a;
	}
 
	public void setA(A a) {
		this.a = a;
	}
}
  1. ApplicationContext.xml在SRC/Main/Resources中创建ApplicationContext.xml,如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true" 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-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<bean id="a" class="org.arpit.theitroad.A">
		 <constructor-arg index="0" ref="b"
	</bean>
	<bean id="b" class="org.arpit.theitroad.B">
		 <constructor-arg index="0" ref="a"
	</bean>
	<context:annotation-config
</beans>

请注意,我们正在使用Contructor注入来在此注入依赖性。
5.创建CirculardependencyMain.java.

package org.arpit.theitroad;
 
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
/**
 * Circular dependencies in Spring
 *
 */
public class CircularDependencyMain 
{
	public static void main( String[] args )
	{
		ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml");
		A a = (A) ac.getBean("a");
		a.doSomeThing();
		ac.close();
		
	}
}

6.在运行上面的程序时运行它,我们将得到以下输出。

Jun 15, 2016 11:21:58 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:21:58 IST 2016]; root of context hierarchy
Jun 15, 2016 11:21:58 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml]
Exception in thread “main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [ApplicationContext.xml]: Cannot resolve reference to bean 'b' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [ApplicationContext.xml]: Cannot resolve reference to bean 'a' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
.
.
.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [ApplicationContext.xml]: Cannot resolve reference to bean 'a' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
.
.
.

正如我们所看到的,我们正在获得BeanCurrinthIncreationException.Requested Bean当前正在创建:是否有一个无法解放的循环引用?

解决方案

这个问题有很多解决方案。

重新设计

我们能重新设计程序以避免循环依赖项。
循环依赖性很糟糕,如果可能的话应该避免。
在无法避免循环依赖性的情况下存在非常罕见的情况。

Setter注射

如果使用Setter注入而不是构造函数注入来解决Spring 中的圆形依赖性,则不会有任何问题。
让我们更改上面的示例并尝试设置设置。
更新a.java.

package org.arpit.theitroad;
 
public class A {
 
	B b;
 
	public B getB() {
		return b;
	}
	public void setB(B b) {
		this.b = b;
	}
	
	public void doSomeThing()
	{
		System.out.println("Doing some work");
	}
}

更新B.Java.

package org.arpit.theitroad;
 
public class B {
	A a;
 
	public A getA() {
		return a;
	}
 
	public void setA(A a) {
		this.a = a;
	}
}

更新applicationscontext.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-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<bean id="a" class="org.arpit.theitroad.A">
		 <property name="b" ref="b"
	</bean>
	<bean id="b" class="org.arpit.theitroad.B">
		 <property name="a" ref="a"
	</bean>
	<context:annotation-config
</beans>

再次运行CirculardendencyMain

Jun 15, 2016 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2016]; root of context hierarchy
Jun 15, 2016 11:35:26 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml]
Doing some work
Jun 15, 2016 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2016]; root of context hierarchy

@lazy with @autowired注释

我们将使用@autowired使用@autowired来解决Spring的循环依赖性。

让我们更改上面的示例并尝试使用@Lazy注释。
更新A.java请注意,我们在此处使用@autowired和@lazy注释。

package org.arpit.theitroad;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
 
public class A {
 
	B b;
 
	@Autowired
	public A(@Lazy B b) {
		super();
		this.b = b;
	}
	
	public B getB() {
		return b;
	}
	public void setB(B b) {
		this.b = b;
	}
	
	public void doSomeThing()
	{
		System.out.println("Doing some work");
	}
}

更新B.Java.

package org.arpit.theitroad;
 
import org.springframework.beans.factory.annotation.Autowired;
 
public class B {
	A a;
 
	@Autowired
	public B(A a) {
		super();
		this.a = a;
	}
 
	public A getA() {
		return a;
	}
 
	public void setA(A a) {
		this.a = a;
	}
}

更新applicationscontext.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-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<bean id="a" class="org.arpit.theitroad.A">
	</bean>
	<bean id="b" class="org.arpit.theitroad.B">
	</bean>
	<context:annotation-config
</beans>

再次运行CirculardendencyMain

Jun 15, 2016 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2016]; root of context hierarchy
Jun 15, 2016 11:35:26 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml]
Doing some work
Jun 15, 2016 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2016]; root of context hierarchy

正如我们所看到的,程序也适用于@Lazy注释。

@PostConstruct与@autowired注释

我们使用@PostConstruct与@Autowifired以解决Spring的循环依赖性。

让我们更改上面的示例并尝试设置设置。
更新A.java请注意,我们在此处使用了@postConstruct和@autowired注释。

package org.arpit.theitroad;
 
import javax.annotation.PostConstruct;
 
import org.springframework.beans.factory.annotation.Autowired;
 
public class A {
 
	@Autowired
	B b;
	
	public B getB() {
		return b;
	}
	public void setB(B b) {
		this.b = b;
	}
	
	
	@PostConstruct
	public void init()
	{
		b.setA(this);
	}
	
	public void doSomeThing()
	{
		System.out.println("Doing some work");
	}
}

更新B.Java.

package org.arpit.theitroad;
 
import org.springframework.beans.factory.annotation.Autowired;
 
public class B {
	A a;
 
	@Autowired
	public B(A a) {
		super();
		this.a = a;
	}
 
	public A getA() {
		return a;
	}
 
	public void setA(A a) {
		this.a = a;
	}
}

更新applicationscontext.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-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<bean id="a" class="org.arpit.theitroad.A">
	</bean>
	<bean id="b" class="org.arpit.theitroad.B">
	</bean>
	<context:annotation-config
</beans>

再次运行CirculardendencyMain

Jun 15, 2016 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2016]; root of context hierarchy
Jun 15, 2016 11:35:26 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml]
Doing some work
Jun 15, 2016 11:35:26 AM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 11:35:26 IST 2016]; root of context hierarchy

正如我们所看到的,程序也适用于@PostContruct注释。