Spring的循环依赖性
如果Spring存在循环依赖性,会发生什么。
问题
Spring有循环依赖性怎么办?
例如:A类需要B类,B类的实例需要A类实例。
让我们创建一个例子。
- 创建一个简单的Java Maven项目。
- 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;
}
}
- 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注释。

