循环依赖
循环依赖是Spring框架中常见的问题之一,当两个或多个类相互引用对方时,就会出现循环依赖的情况。这种情况下,Spring框架无法确定哪个类应该先实例化和初始化,从而导致异常。常见的解决方法有:构造函数注入、setter方法注入、静态工厂方法注入以及使用第三方库等。
本次使用版本:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.0</version> <relativePath/> </parent>
案例
public interface ServiceA { } public interface ServiceB { } @Service public class ServiceAImpl implements ServiceA { private ServiceB serviceB; public ServiceAImpl(ServiceB serviceB) { this.serviceB = serviceB; } } @Service public class ServiceBImpl implements ServiceB { private ServiceA serviceA; public ServiceBImpl(ServiceA serviceA) { this.serviceA = serviceA; } }
解决方法
1.重新设计
当有一个循环依赖,很可能是有一个设计问题并且各责任没有得到很好的分离。应该尽量正确地重新设计组件,以便它们的层次是精心设计的,也没有必要循环依赖。
如果不能重新设计组件(可能有很多的原因:遗留代码,已经被测试并不能修改代码,没有足够的时间或资源来完全重新设计…),但有一些变通方法来解决这个问题。
2.@Lazy
解决Spring 循环依赖的一个简单方法就是对一个Bean使用延时加载。也就是说:这个Bean并没有完全的初始化完,实际上他注入的是一个代理,只有当他首次被使用的时候才会被完全的初始化。
@Service public class ServiceAImpl implements ServiceA { private ServiceB serviceB; public ServiceAImpl(@Lazy ServiceB serviceB) { this.serviceB = serviceB; } }
3.Setter/Field 注入
简单地说,你对你需要注入的bean是使用setter注入(或字段注入),而不是构造函数注入。通过这种方式创建Bean,实际上它此时的依赖并没有被注入,只有在你须要的时候他才会被注入进来。
@Service public class ServiceAImpl implements ServiceA { private ServiceB serviceB; @Autowired public void setServiceB(ServiceB serviceB) { this.serviceB = serviceB; } }
SpringBoot 2.6.x不推荐使用循环依赖,最简单的方式是在全局配置文件中允许循环引用存在,此属性默认值为false,显示声明为true,可回避项目启动时控制台循环引用异常。
spring: main: allow-circular-references: true
4.@PostConstruct
打破循环的另一种方式是:在要注入的属性(该属性是一个bean)上使用 @Autowired
,并使用@PostConstruct
标注在另一个方法,且该方法里设置对其他的依赖。
@Service public class ServiceAImpl implements ServiceA { @Autowired private ServiceB serviceB; @PostConstruct public void init() { System.out.println(serviceB); serviceB.setServiceA(this); } } @Service public class ServiceBImpl implements ServiceB { private ServiceA serviceA; public void setServiceA(ServiceA serviceA) { System.out.println(serviceA); this.serviceA = serviceA; } }
总结:
方式 | 依赖情况 | 注入方式 | 能够解决循环依赖 |
情况一 | AB相互依赖 | 均采用setter方式 | 能 |
情况二 | AB相互依赖 | 均采用构造器方式 | 不能 |
情况三 | AB相互依赖 | A中注入B采用setter,B中注入A采用构造器 | 能 |
情况四 | AB相互依赖 | A中注入B采用构造器,B中注入A采用setter | 不能 |
情况五 | AB相互依赖 | A中注入B采用@Autowired ,B中注入A采用@PostConstruct + setter |
能 |
情况六 | AB相互依赖 | A中注入B采用@PostConstruct + setter,B中注入A采用@Autowired |
能 |