• zxb的博客
    • 运维
      • 🧊即插即更:移动硬盘与 U 盘的自动同步方案
      • ⛳FRP穿透个人博客——SSL安全篇
      • 📄Github Action自动化部署Vue3项目
      • 🎲Docker Desktop 代理配置:让镜像拉取更稳更快
      • 🤓FRP穿透搭建个人博客(白嫖SSL版)
      • 🪁FRP穿透搭建个人博客
      • 📄Kubeeasy安装K8s集群(附独家报错解决)
      • 📄不用 Kubernetes,Docker Compose 也能实现零停机蓝绿发布
    • 技术体验
      • 🛡️别再找插件了,美团出品的Tabbit才是真正的AI浏览器
      • 💧GitHub 霸榜!Tabbit平替:给浏览器装上“最强大脑”,这才是真·AI 浏览器插件
    • 自制软件插件
      • 🧋🧧 仪式感拉满!这款开源“年味”小游戏,带你瞬间找回童年快乐!
      • 🕸️摸鱼神器——摸了吗
      • 🔍🚀 思源笔记 S3 插件 v1.0.2 更新:手把手教你配置 PicList 导出
      • 🌊🚀 思源笔记 S3 插件 v1.0.3 更新:一键解锁 BM.md 精美排版!
      • 🥔AE机器人大模型案例
      • Claude Code 终于会"叫"了 —— 一个 10MB 小工具,让 AI 跑完任务发个声
    • 开发小技巧
      • 🪴【保姆级】NAS 骚操作:白嫖百 T 网盘做图床!阿里云/百度秒变“私有云相册”,快到飞起!
    • 后端技术
      • 🚁解决 Spring Session 分布式部署难题:Redis 集成指南
      • 📄使用ThreadLocal实现用户身份认证
      • SpringAI
        • 别再手写 HTTP 客户端调 AI 了!Spring AI 官方出手,一行代码搞定多模型切换
      • 📄使用注解+反射实现自动填充
      • Spring
        • 🔁循环依赖:一个Spring经典坑
        • Spring如何解决依赖循环
        • 🫛什么是Spring Bean
      • Java基础
        • 什么是序列化和反序列化?
        • 📄Java中HashMap的原理
      • 📄分布式系统中的"保险箱":事务Outbox模式深度解析
    • 📑前端技术
      • 🫚axios工具类
      • 🍛Vite项目屏幕适配的两种方案,超详细
      • 📕vue-router小技巧:通过route传参动态设置页面
      • 📄Next.js 中 NEXT_PUBLIC_ 环境变量为何修改后不生效
    • 疑难杂症
      • 🕙SpringWeb报错——CORS问题解决
      • 📄一行 JVM 参数解决 HttpClient 卡死:强制 Java 禁用 IPv6
      • 📄修复github action注入npm包权限问题
zxb的博客后端技术Spring

循环依赖:一个Spring经典坑

访问次数 2677 次创建时间 2026-03-27 11:08

当你有两个 service​ 时,他们的业务互相关联,比如:用户service需要查询订单service,同时订单service又需要通过用户service来查询用户

这样就会形成一个闭环,如下:

image

‍

实际代码demo

UserService:

@Service
public class UserService {

    @Autowired
    private OrderService orderService;

    public Order getUserOrder(Long userId) {
        return orderService.getOrderByUser(userId);
    }

    public User getUser(Long userId) {
        // 模拟查询后User
        User user = new User();
        user.setId(1L);
        return user;
    }

}

‍

OrderService:

@Service
public class OrderService {

    @Autowired
    private UserService userService;

    public Order getOrderByUser(Long userId) {
        User user = userService.getUser(userId);
        Order order = new Order();
        order.setOrderId("xxx");
        order.setAmount(100L);
        order.setUserId(user.getId());
        return order;
    }

}

‍

使用时,会报错如下:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService': Unsatisfied dependency expressed through field 'userService': Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'orderService': Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:767)
.....

‍

临时解决方案

给 UserService​依赖添加 @lazy

@Lazy
@Autowired
private UserService userService;

‍

[!TIP]

什么是 @lazy

@Lazy​ 是 Spring 框架中的一个注解,用于实现 Bean 的延迟初始化。


核心概念

默认行为 vs @Lazy

场景 初始化时机
默认(无@Lazy) Spring 容器启动时立即创建 Bean
使用 @Lazy 首次从容器获取 Bean 时才创建

使用方式

1️⃣ 类级别使用

@Component
@Lazy  // 整个类延迟初始化
public class ExpensiveService {
    public ExpensiveService() {
        System.out.println("ExpensiveService 被创建了!");
        // 假设这里有耗时的初始化操作
    }

    public void doSomething() {
        System.out.println("执行操作...");
    }
}

2️⃣ 配置类中使用

@Configuration
public class AppConfig {

    @Bean
    @Lazy  // 该 Bean 延迟初始化
    public DataSource dataSource() {
        System.out.println("创建数据库连接池...");
        return new HikariDataSource();
    }
}

3️⃣ 注入时使用(推荐)

@Service
public class OrderService {

    private final ExpensiveService expensiveService;

    @Autowired
    @Lazy  // 注入时标记为延迟加载
    public OrderService(ExpensiveService expensiveService) {
        this.expensiveService = expensiveService;
    }
}

工作原理图解

┌─────────────────────────────────────────────────────┐
│                  Spring 容器启动                      │
├─────────────────────────────────────────────────────┤
│                                                      │
│   @Component          @Component @Lazy               │
│   ┌─────────┐         ┌─────────────┐               │
│   │ ServiceA │         │  ServiceB   │ ← 不创建!    │
│   │  ✓创建   │         │  (仅注册)    │               │
│   └─────────┘         └─────────────┘               │
│                                                      │
└─────────────────────────────────────────────────────┘
                         │
                         ▼ 当首次使用 ServiceB 时
┌─────────────────────────────────────────────────────┐
│              ServiceB 首次被调用                       │
├─────────────────────────────────────────────────────┤
│                                                      │
│   ┌─────────────┐                                    │
│   │  ServiceB   │ ← 此时才真正创建!                  │
│   │   ✓创建     │                                    │
│   └─────────────┘                                    │
│                                                      │
└─────────────────────────────────────────────────────┘

实际应用场景

场景1:解决循环依赖

@Service
public class ServiceA {
    @Autowired
    @Lazy  // 打破循环依赖
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

场景2:重量级资源延迟加载

@Service
public class ReportService {

    @Lazy
    @Autowired
    private MLModelService mlModelService; // 加载大型机器学习模型

    public Report generateReport() {
        // 只有在生成报告时才加载模型
        return mlModelService.analyze();
    }
}

场景3:可选功能

@Component
public class PaymentService {

    @Lazy
    @Autowired(required = false)
    private Optional<wechatpayservice> wechatPay; // 可选的微信支付

    public void pay() {
        wechatPay.ifPresent(service -> service.pay());
    }
}

全局延迟配置(Spring Boot)

在 application.yml 中可以全局开启延迟初始化:

spring:
  main:
    lazy-initialization: true  # 所有 Bean 都延迟初始化

注意事项

注意点 说明
单例特性不变 @Lazy Bean 仍然是单例,首次创建后会缓存
@Lazy 失效情况 被 @PostConstruct​、SmartInitializingSingleton 等主动触发时可能失效
测试困难 延迟加载的 Bean 在测试中可能需要额外处理
启动快,首次慢 启动时间缩短,但首次调用会有延迟

总结

@Lazy = 推迟 Bean 的创建时机

何时使用?
├── 解决循环依赖
├── 减少启动时间(轻量级应用)
├── 延迟加载重量级资源
└── 可选/按需加载的功能

‍

Spring2.6前的情况

在Spring2.6前,Spring遇到这种情况会自动帮你兜底,除了你自己用构造器创建的bean。

但后续删了这个特性,因为这样子的情况本就是设计问题,使用 @lazy 只是缓兵之计。

‍

如何避免循环依赖

image

‍

评论

0 条评论

暂无评论,欢迎第一个留言。

验证码
回复评论
验证码
举报内容
验证码
由 b8l8u8e8 提供支持