Spring 中的循环依赖

x33g5p2x  于9个月前 转载在 Spring  
字(4.3k)|赞(0)|评价(0)|浏览(110)

这篇文章是关于如何解决 Spring 中导致 UnsatisfiedDependencyException 的循环依赖问题。简而言之,当两个服务相互依赖时会导致循环依赖问题。

循环依赖是在依赖注入期间spring-context尝试加载对象并且一个bean依赖另一个bean时引起的问题。假设当对象 A 和 B 相互依赖时,即 A 依赖于 B,反之亦然。 Spring 在创建 A 和 B 的对象时抛出 UnsatisfiedDependencyException ,因为除非创建 B,否则无法创建 A 对象,反之亦然。

让我们使用真实的代码示例来理解它。创建两个服务 ServiceAServiceB 并尝试将 ServiceA 注入 ServiceB,反之亦然,如上图所示。

ServiceA.java

package org.websparrow.service;

import org.springframework.stereotype.Service;

@Service
public class ServiceA {

	private ServiceB serviceB;

	public ServiceA(ServiceB serviceB) {

		System.out.println("Calling Service A");

		this.serviceB = serviceB;
	}
}

ServiceB.java

package org.websparrow.service;

import org.springframework.stereotype.Service;

@Service
public class ServiceB {

	private ServiceA serviceA;

	public ServiceB(ServiceA serviceA) {

		System.out.println("Calling Service B");

		this.serviceA = serviceA;
	}
}

要模拟循环依赖问题,请运行以下类,并查看控制台日志。

CircularDependenciesTestApp.java

package org.websparrow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CircularDependenciesTestApp {

	public static void main(String[] args) {
		SpringApplication.run(CircularDependenciesTestApp.class, args);
	}
}

当我们执行 CircularDependenciesTestApp 类时,由于相互之间的循环依赖,它将无法注入依赖项,并且会抛出一个 checked 异常,如下所示:

控制台日志

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-05-27 21:22:46.368 ERROR 4480 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  serviceA defined in file [F:\sts4-workspace\circular-dependencies-spring\target\classes\org\websparrow\service\ServiceA.class]
↑     ↓
|  serviceB defined in file [F:\sts4-workspace\circular-dependencies-spring\target\classes\org\websparrow\service\ServiceB.class]
└─────┘

###如何解决这个问题?

要解决循环依赖问题,您有两种选择:

1. 在构造函数注入中使用@Lazy

我们可以在构造函数注入期间延迟初始化 ServiceB bean,以延迟构造 ServiceB bean。为了更清晰,以下是 ServiceA 中的代码更改:
ServiceA.java

package org.websparrow.service;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {

	private ServiceB serviceB;

	public ServiceA(@Lazy ServiceB serviceB) {

		System.out.println("Calling Service A");

		this.serviceB = serviceB;
	}
}

如果你再次运行 CircularDependenciesTestApp 类,你会发现循环依赖问题解决了。

控制台日志

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

2020-05-27 21:33:22.637  INFO 7156 --- [           main] o.w.CircularDependenciesTestApp          : Starting CircularDependenciesTestApp on Atul-PC with PID 7156 (F:\sts4-workspace\circular-dependencies-spring\target\classes started by Atul in F:\sts4-workspace\circular-dependencies-spring)
2020-05-27 21:33:22.640  INFO 7156 --- [           main] o.w.CircularDependenciesTestApp          : No active profile set, falling back to default profiles: default
Calling Service A
Calling Service B
2020-05-27 21:33:23.251  INFO 7156 --- [           main] o.w.CircularDependenciesTestApp          : Started CircularDependenciesTestApp in 0.98 seconds (JVM running for 1.667)

2. 使用@Autowired 和@Lazy 注释

使用 @Autowired@Lazy 注释将 ServiceB 注入 ServiceA。让我们使用这些注解来注入 bean 并测试我们的应用程序是否解决了问题:
ServiceA.java

package org.websparrow.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {

	@Autowired
	@Lazy
	private ServiceB serviceB;

	/*
	public ServiceA(ServiceB serviceB) {
		System.out.println("Calling Service A");
		this.serviceB = serviceB;
	}
	*/
}

这是再次运行 CircularDependenciesTestApp 类时控制台日志上的输出:

控制台日志

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

2020-05-27 21:45:07.583  INFO 4036 --- [           main] o.w.CircularDependenciesTestApp          : Starting CircularDependenciesTestApp on Atul-PC with PID 4036 (F:\sts4-workspace\circular-dependencies-spring\target\classes started by Atul in F:\sts4-workspace\circular-dependencies-spring)
2020-05-27 21:45:07.586  INFO 4036 --- [           main] o.w.CircularDependenciesTestApp          : No active profile set, falling back to default profiles: default
Calling Service B
2020-05-27 21:45:08.141  INFO 4036 --- [           main] o.w.CircularDependenciesTestApp          : Started CircularDependenciesTestApp in 0.928 seconds (JVM running for 1.614)

结论

在本教程中,我们了解了什么是循环依赖,它何时出现在应用程序中以及如何解决它。

相关文章

热门文章

更多