Spring中循环依赖解决方案

简介: Spring中循环依赖解决方案

循环依赖

 循环依赖是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
相关文章
|
2月前
|
设计模式 Java 开发者
解密Spring:优雅解决依赖循环的神兵利器
解密Spring:优雅解决依赖循环的神兵利器
192 57
|
14天前
|
JavaScript Java Maven
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
66 1
|
2月前
|
缓存 Java 开发工具
【spring】如何解决循环依赖
【spring】如何解决循环依赖
161 56
|
3天前
|
Java Spring
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException(Spring循环依赖问题)
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException(Spring循环依赖问题)
|
27天前
|
Java 应用服务中间件 微服务
spring boot 中Feign调用提示Request header is too large 解决方案
spring boot 中Feign调用提示Request header is too large 解决方案
18 1
|
1月前
|
缓存 Java uml
Spring压轴题:当循环依赖遇上Spring AOP
Spring压轴题:当循环依赖遇上Spring AOP
21 1
|
2月前
|
JSON 安全 前端开发
跨域详解及Spring Boot 3中的跨域解决方案
本文介绍了Web开发中的跨域问题,包括概念、原因、影响以及在Spring Boot 3中的解决方案。跨域是由浏览器的同源策略限制引起的,阻碍了不同源之间的数据传输。解决方法包括CORS、JSONP和代理服务器。在Spring Boot 3中,可以通过配置CorsFilter来允许跨域请求,实现前后端分离项目的正常运行。
77 3
 跨域详解及Spring Boot 3中的跨域解决方案
|
1月前
|
Java Spring 缓存
Spring Bean循环依赖详解
【6月更文挑战第2天】
30 2
|
1月前
|
负载均衡 Java API
Spring Cloud Gateway 详解:构建高效的API网关解决方案
Spring Cloud Gateway 详解:构建高效的API网关解决方案
30 0
|
5天前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
28 0