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注释。