所有分类
  • 所有分类
  • 未分类

Spring-循环依赖的解决方案-实例

简介

说明

本文用实例介绍如何解决Spring的循环依赖问题(SpringBoot2.6之前的版本)。

相关网址

Spring–循环依赖导致启动失败 – 自学精灵

公共代码

package com.example.controller;

import com.example.tmp.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    private A a;

    @GetMapping("/test1")
    public String test1() {
        return a.getTest();
    }
}

方案1. Feild注入单例(@AutoWired)

代码

package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class A {
    @Autowired
    private B b;
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
}
package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    @Autowired
    private A a;
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }

}

测试

启动不报错。

postman访问:http://localhost:8080/test1

后端结果:不报错

postman结果: 20Tony

方案2. 构造器注入+@Lazy

延迟加载:在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入。

代码

package com.example.tmp;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
 
@Component
public class A {
    private B b;

    public A(@Lazy B b) {
        this.b = b;
    }
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }
}
package com.example.tmp;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    private A a;

    public B(@Lazy A a) {
        this.a = a;
    }
 
    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }

}

测试

启动不报错。

postman访问:http://localhost:8080/test1

后端结果:不报错

postman结果: 20Tony

方案3. Setter/Field注入单例

代码

package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class A {
    private B b;
 
    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }

    public B getB() {
        return b;
    }

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}
package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    private A a;

    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }

    public A getA() {
        return a;
    }

    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}

测试 

启动不报错。

postman访问:http://localhost:8080/test1

后端结果:不报错

postman结果: 20Tony

方案4. 实现ApplicationContextAware与InitializingBean

代码 

package com.example.tmp;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class A implements ApplicationContextAware, InitializingBean {
    private B b;

    private ApplicationContext context;

    private String name = "Tony";
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getTest() {
        return b.getAge().toString() + name;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.b = context.getBean(B.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}
package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class B {
    @Autowired
    private A a;

    private Integer age = 20;
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
}

测试 

启动不报错。

postman访问:http://localhost:8080/test1

后端结果:不报错

postman结果: 20Tony

注意

SpringBoot2.6版本开始,已默认禁止了循环依赖,必须要加个配置才能启用循环依赖:

application.yml添加如下配置:

spring
  main
    allow-circular-references: true 
5

评论8

请先

  1. springboot版本2.7,方法1直接启动报错。需要添加allow-circular-references参数配置,这种方法和在类上添加@Lazy哪种效果好
    珠光2023 2024-01-09 1
    • SpringBoot2.6开始,默认禁止循环依赖了。用allow-circular-references参数开启循环依赖,这种比较好。
      自学精灵 2024-01-09 1
  2. Springboot中两个service@Autowired互相引入,循环依赖是怎么回事
    珠光2023 2023-11-27 0
    • 应该是你的SpringBoot版本问题。2.6之后禁止了循环依赖,启用方法我更新到此文章中的最后边了。
      自学精灵 2023-11-27 0
  3. 所以是没有spring+@PostConstruct 解决循环依赖的 方案对吗
    秋风扫落叶 2023-08-04 0
    • 目前是的,如果后边发现了,我会更新到文章中并评论到这里。目前文中的四种方案,无论是面试还是实际工作,都够用了。
      自学精灵 2023-08-04 1
  4. 方案四和方案一没看出区别啊。方案四中的@PostConstruct相关代码去掉后跟方案一一模一样,感觉不加这块代码也可以解决依赖循环啊
    秋风扫落叶 2023-08-03 0
    • 确实是,之前总结的有点问题,现在修改好了。
      自学精灵 2023-08-03 0
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录