Spring循环依赖导致Bean无法正确初始化

1. 报错信息如下

dart 复制代码
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webConfig' defined in file [E:\code_repository\target\classes\com\kym\kymconfig\config\WebConfig.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsServiceImpl': Unsatisfied dependency expressed through field 'dictService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dictServiceImpl': Unsatisfied dependency expressed through field 'aService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AServiceImpl': Unsatisfied dependency expressed through field 'orderService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl': Unsatisfied dependency expressed through field 'order'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.kym.kymconfig.sdk.ISdkOrder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

2. 代码如下

java 复制代码
@Configuration
@Order(1)
public class WebConfig {
    private UserDetailsService userDetailsService;

    @Bean
    public Map<String, Object> initMap() {
        return new HashMap<>();
    }

    public WebConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}

@Order(2)
@Configuration
public class SdkConfig {
    @Autowired
    private DictService dictService;

    @Bean
    public ISdkOrder init() {
        try {
            int dict = dictService.getDict();
            return new SdkOrder(dict);
        } catch (Exception e) {
            return null;
        }
    }
}

public interface ISdkOrder {

    void track(@NonNull int eventRecord);
}


public interface AService {
    int doS();
}


public interface DictService {

    int getDict();
}

public interface OrderService {

    void track(int num);
}

public interface UserDetailsService {
    int loadUserByUsername(String username);
}

public class SdkOrder implements ISdkOrder {
    private final int worker;

    public SdkOrder(final int sdkProducer) {
        worker = sdkProducer;
    }

    @Override
    public void track(@NonNull int eventRecord) {
        System.out.println(eventRecord);
    }
}


@Service
public class AServiceImpl implements AService {
    @Autowired
    private OrderService orderService;
    @Override
    public int doS() {
        orderService.track(8);
        return 0;
    }
}

@Service
public class DictServiceImpl implements DictService {
    @Autowired
    private AService aService;
    @Override
    public int getDict() {
        aService.doS();
        return 5;
    }
}

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    @Lazy
    private ISdkOrder order;
    @Override
    public void track(int num) {
        order.track(num);
    }
}

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private DictService dictService;
    @Override
    public int loadUserByUsername(String username) {
        dictService.getDict();
        return 1;
    }
}

3. 依赖链分析

  1. 完整循环路径

    text 复制代码
    WebConfig → UserDetailsServiceImpl → DictServiceImpl → AServiceImpl → OrderServiceImpl → IOrder(需SdkConfig初始化)

    最终 SdkConfig 又依赖 DictService 形成闭环。

  2. 关键问题点

    • OrderServiceImpl 需要注入 ISdkOrder,但 ISdkOrder 的初始化依赖 DictService
    • DictService 又通过 AService 间接依赖 OrderService,形成死锁

4. 解决方案

方案:打破循环依赖

java 复制代码
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    @Lazy
    private ISdkOrder order;
    @Override
    public void track(int num) {
        order.track(num);
    }
}

在自动注入ISdkOrder时添加@Lazy注解。

这样解决了循环依赖问题,原因在于 @Lazy 注解改变了 Spring 的依赖注入行为。具体原理如下:

  1. 延迟加载机制
    @Lazy 使 Spring 不会在容器启动时立即注入真实的 ISdkOrder Bean,而是先注入一个代理对象。只有当第一次实际调用 order.track() 方法时,才会触发真实 Bean 的初始化。

  2. 打破初始化死锁

    原始循环依赖链:

    text 复制代码
    OrderServiceImpl → ISdkOrder → DictService → AService → OrderServiceImpl

    添加 @Lazy 后,ISdkOrder 的初始化被推迟,使得其他 Bean 能先完成初始化。

  3. 代理对象特性

    Spring 会生成一个 CGLIB 代理对象暂时代替 ISdkOrder,这个代理对象不依赖 DictService 等下游组件,因此容器可以顺利完成初始化阶段。

  4. 运行时解析

    当实际调用 track() 方法时,代理对象会检查真实 Bean 是否已初始化。此时其他组件都已就绪,可以安全创建真实 ISdkOrder 实例。

这种方案适用于需要保留原有代码结构的场景,但要注意:

  • 代理对象会增加少量性能开销
  • 首次调用可能出现短暂延迟
  • 需确保被 @Lazy 标注的字段不会在 @PostConstruct 方法中使用
相关推荐
IT_陈寒8 小时前
Python开发者必知的5大性能陷阱:90%的人都踩过的坑!
前端·人工智能·后端
流浪克拉玛依9 小时前
Go Web 服务限流器实战:从原理到压测验证 --使用 Gin 框架 + Uber Ratelimit / 官方限流器,并通过 Vegeta 进行性能剖析
后端
孟沐9 小时前
保姆级教程:手写三层架构 vs MyBatis-Plus
后端
星浩AI9 小时前
让模型自己写 Skills——从素材到自动生成工作流
人工智能·后端·agent
华仔啊11 小时前
为啥不用 MP 的 saveOrUpdateBatch?MySQL 一条 SQL 批量增改才是最优解
java·后端
武子康12 小时前
大数据-242 离线数仓 - DataX 实战:MySQL 全量/增量导入 HDFS + Hive 分区(离线数仓 ODS
大数据·后端·apache hive
砍材农夫12 小时前
TCP和UDP区别
后端
千寻girling13 小时前
一份不可多得的 《 Django 》 零基础入门教程
后端·python·面试
千寻girling13 小时前
Python 是用来做 AI 人工智能 的 , 不适合开发 Web 网站 | 《Web框架》
人工智能·后端·算法
贾铭13 小时前
如何实现一个网页版的剪映(三)使用fabric.js绘制时间轴
前端·后端