Springboot项目中循环依赖的问题

相信很多做过Java SpringBoot项目的人都会遇过循环依赖的问题。

循环依赖产生的原因

循环依赖的本质是两个服务的职责边界不清晰,或直接依赖了对方的完整实例。

举个例子:

复制代码
UserOrgRelationService
java 复制代码
package com.example.druidtest.service;

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

import java.util.ArrayList;
import java.util.List;

/**
 * @author chenshixian
 **/

@Service
public class UserOrgRelationService {

    @Autowired
    private UserService userService;

    @Autowired
    private OrgService orgService;


    // 公共逻辑:根据用户ID查所属机构、根据机构ID查用户列表等
    public List<Long> getOrgIdsByUserId(Long userId) {
        // 实现查询逻辑
        return new ArrayList<>();
    }

    public List<Long> getUserIdsByOrgId(Long orgId) {
        // 实现查询逻辑
        return new ArrayList<>();
    }

    public void addUserOrgRelation(Long userId, Long orgId) {
        if (userService.getUserById(userId) == null) {
            throw new RuntimeException("用户不存在");
        }
        if (orgService.getOrgById(orgId) == null) {
            throw new RuntimeException("用户不存在");
        }
    }

}
java 复制代码
// 2. 用户服务:依赖公共组件,不再直接依赖机构服务
@Service
public class UserService {
    @Autowired
    private UserOrgRelationService userOrgRelationService;

    // 用户服务的核心逻辑
    public User getUserById(Long userId) {
        // 如需机构相关数据,调用公共组件
        List<Long> orgIds = userOrgRelationService.getOrgIdsByUserId(userId);
        // 其他逻辑
        return new User();
    }
}

// 3. 机构服务:依赖公共组件,不再直接依赖用户服务
@Service
public class OrgService {
    @Autowired
    private UserOrgRelationService userOrgRelationService;

    // 机构服务的核心逻辑
    public Org getOrgById(Long orgId) {
        // 如需用户相关数据,调用公共组件
        List<Long> userIds = userOrgRelationService.getUserIdsByOrgId(orgId);
        // 其他逻辑
        return new Org();
    }
}

这个时候 ,后台就会报循环依赖:

这里UserOrgRelationService依赖了UserService和OrgService,而UserService又依赖了UserOrgRelationService,就产生了循环依赖。

解决方案

1. 逻辑分拆

这里可以逻辑都加载UserService里面。

java 复制代码
package com.example.druidtest.service;

import com.example.druidtest.entity.Org;
import com.example.druidtest.entity.User;
import com.example.druidtest.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author chenshixian
 **/

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserOrgRelationService userOrgRelationService;

    @Autowired
    private OrgService orgService;

    // 用户服务的核心逻辑
    public User getUserById(Long userId) {
        // 如需机构相关数据,调用公共组件
        List<Long> orgIds = userOrgRelationService.getOrgIdsByUserId(userId);
        // 其他逻辑
        return new User();
    }

    public void addUser(User user, Long orgId) {
        userRepository.save(user);

        Org org = orgService.getOrgById(orgId);
        if (org != null) {
            throw new RuntimeException("机构不存在");
        }

        userOrgRelationService.add(user.getId(), orgId);
    }
}
方案 2:使用 Spring 上下文延迟获取 Bean(应急方案)

如果暂时无法拆分逻辑,可以通过 ApplicationContext 延迟获取依赖的 Bean,打破初始化阶段的循环依赖(本质是 "懒加载",而非真正解耦)。

示例实现:

复制代码
@Service
public class UserService {
    // 注入Spring上下文
    @Autowired
    private ApplicationContext applicationContext;

    // 业务方法中延迟获取机构服务
    public void doUserBiz(Long userId) {
        // 不在初始化阶段获取,而是在使用时获取
        OrgService orgService = applicationContext.getBean(OrgService.class);
        orgService.queryOrgByUserId(userId);
        // 其他业务逻辑
    }
}

@Service
public class OrgService {
    @Autowired
    private ApplicationContext applicationContext;

    public void doOrgBiz(Long orgId) {
        UserService userService = applicationContext.getBean(UserService.class);
        userService.queryUserByOrgId(orgId);
        // 其他业务逻辑
    }
}
方案 3:使用 @Lazy 懒加载(适用于构造器注入场景)

如果必须使用构造器注入(Spring 推荐的注入方式),可以通过 @Lazy 注解让依赖的 Bean 延迟初始化,避免初始化阶段的循环依赖。

示例实现:

复制代码
@Service
public class UserService {
    private final OrgService orgService;

    // 构造器注入时添加@Lazy,延迟加载OrgService
    public UserService(@Lazy OrgService orgService) {
        this.orgService = orgService;
    }

    public void queryUserByOrgId(Long orgId) {
        // 业务逻辑
    }
}

@Service
public class OrgService {
    private final UserService userService;

    // 同理,延迟加载UserService
    public OrgService(@Lazy UserService userService) {
        this.userService = userService;
    }

    public void queryOrgByUserId(Long userId) {
        // 业务逻辑
    }
}
方案 4:使用 setter 注入(Spring 原生支持)

Spring 对单例 Bean 的 setter 注入天然支持循环依赖(通过三级缓存解决),这是最基础的解决方案,但可读性和规范性不如前几种。

示例实现:

复制代码
@Service
public class UserService {
    private OrgService orgService;

    // setter注入(而非构造器/字段注入)
    @Autowired
    public void setOrgService(OrgService orgService) {
        this.orgService = orgService;
    }

    // 业务方法
    public void doBiz() {
        orgService.queryOrg();
    }
}

@Service
public class OrgService {
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    // 业务方法
    public void queryOrg() {
        // 逻辑
    }
}
相关推荐
非优秀程序员6 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻7 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
沐晓时光7 小时前
C语言入门,内存函数知识讲解
c语言·开发语言
文丑颜不良啊9 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang10 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪10 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息11 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼11 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff12 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff13 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc