• 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

访问次数 2667 次创建时间 2026-03-27 11:08
  • 🔁 什么是循环依赖[^1]
  • 📄 未命名[^2]

‍

[^1]: # 循环依赖:一个Spring经典坑

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

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

![image](/uploads/shares/2/assets/image-20260528132847-vtt1zl6.png)

‍

## 实际代码demo

`UserService`:

```java
@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`:

```java
@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;
    }

}
```
‍

使用时,会报错如下:

```log
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`

```java
@Lazy
@Autowired
private UserService userService;
```
‍

> [!TIP]
> ## 什么是 @lazy
>
> `@Lazy`​ 是 **Spring 框架**中的一个注解,用于实现 **Bean 的延迟初始化**。
>
> ---
>
> ### 核心概念
>
> #### 默认行为 vs @Lazy
>
> |场景|初始化时机|
> | ------| --------------------------------|
> |**默认(无@Lazy)**|Spring 容器启动时立即创建 Bean|
> |**使用 @Lazy**|首次从容器获取 Bean 时才创建|
>
> ---
>
> ### 使用方式
>
> #### 1️⃣ 类级别使用
>
> ```java
> @Component
> @Lazy  // 整个类延迟初始化
> public class ExpensiveService {
>     public ExpensiveService() {
>         System.out.println("ExpensiveService 被创建了!");
>         // 假设这里有耗时的初始化操作
>     }
>     
>     public void doSomething() {
>         System.out.println("执行操作...");
>     }
> }
> ```
> #### 2️⃣ 配置类中使用
>
> ```java
> @Configuration
> public class AppConfig {
>     
>     @Bean
>     @Lazy  // 该 Bean 延迟初始化
>     public DataSource dataSource() {
>         System.out.println("创建数据库连接池...");
>         return new HikariDataSource();
>     }
> }
> ```
> #### 3️⃣ 注入时使用(推荐)
>
> ```java
> @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:解决循环依赖
>
> ```java
> @Service
> public class ServiceA {
>     @Autowired
>     @Lazy  // 打破循环依赖
>     private ServiceB serviceB;
> }
>
> @Service
> public class ServiceB {
>     @Autowired
>     private ServiceA serviceA;
> }
> ```
> #### 场景2:重量级资源延迟加载
>
> ```java
> @Service
> public class ReportService {
>     
>     @Lazy
>     @Autowired
>     private MLModelService mlModelService; // 加载大型机器学习模型
>     
>     public Report generateReport() {
>         // 只有在生成报告时才加载模型
>         return mlModelService.analyze();
>     }
> }
> ```
> #### 场景3:可选功能
>
> ```java
> @Component
> public class PaymentService {
>     
>     @Lazy
>     @Autowired(required = false)
>     private Optional<wechatpayservice> wechatPay; // 可选的微信支付
>     
>     public void pay() {
>         wechatPay.ifPresent(service -> service.pay());
>     }
> }
> ```
> ---
>
> ### 全局延迟配置(Spring Boot)
>
> 在 `application.yml` 中可以全局开启延迟初始化:
>
> ```yaml
> spring:
>   main:
>     lazy-initialization: true  # 所有 Bean 都延迟初始化
> ```
> ---
>
> ### 注意事项
>
> |注意点|说明|
> | --------| ------------------------------------------|
> |**单例特性不变**|@Lazy Bean 仍然是单例,首次创建后会缓存|
> | **@Lazy 失效情况**|被 `@PostConstruct`​、`SmartInitializingSingleton` 等主动触发时可能失效|
> |**测试困难**|延迟加载的 Bean 在测试中可能需要额外处理|
> |**启动快,首次慢**|启动时间缩短,但首次调用会有延迟|
>
> ---
>
> ### 总结
>
> ```
> @Lazy = 推迟 Bean 的创建时机
>
> 何时使用?
> ├── 解决循环依赖
> ├── 减少启动时间(轻量级应用)
> ├── 延迟加载重量级资源
> └── 可选/按需加载的功能
> ```
>

‍

## Spring2.6前的情况

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

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

‍

## 如何避免循环依赖

![image](/uploads/shares/2/assets/image-20260528165741-hfyomim.png)

‍

[^2]: # 什么是Spring Bean

> 由Spring全权管理的“对象”容器
>
> 普通的对象:自己new自己管理
>

# Spring Bean 的两种常见定义方式

## 一、组件扫描(Component Scanning)

通过**注解 + 自动扫描**的方式,让 Spring 自动发现并注册 Bean。

### 核心注解

|注解|语义|典型场景|
| -----------| ------------| ------------------|
|`@Component`|通用组件|工具类、通用服务|
|`@Service`|业务逻辑层|Service 层|
|`@Repository`|数据访问层|DAO / Mapper 层|
|`@Controller`​ / `@RestController`|控制器层|Web 接口层|

### 示例

```java
// 1. 标注注解 → 声明"我是一个 Bean"
@Service
public class UserService {

    public User findById(Long id) {
        // ...
    }
}

// 2. 配置扫描路径(Spring Boot 自动处理)
@SpringBootApplication  // 内含 @ComponentScan
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}
```
### 工作流程

```
Spring 启动
    │
    ▼
扫描指定包路径下所有类
    │
    ▼
发现 @Component / @Service / @Repository / @Controller
    │
    ▼
自动创建实例并注册到 IoC 容器
    │
    ▼
Bean 可用,支持 @Autowired 注入
```
---

## 二、配置类(Java Config / @Bean)

通过 `@Configuration`​ + `@Bean`​ **手动声明** Bean 的创建逻辑。

### 示例

```java
@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setUrl("jdbc:mysql://localhost:3306/mydb");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    // 方法名就是 Bean 的名称,也可以自定义
    @Bean("myCache")
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("users");
    }
}
```
### 工作流程

```
Spring 启动
    │
    ▼
发现 @Configuration 类
    │
    ▼
执行所有 @Bean 方法
    │
    ▼
将返回对象注册到 IoC 容器
    │
    ▼
Bean 可用,支持 @Autowired 注入
```
---

## 三、核心对比

```
┌──────────────┬─────────────────────┬──────────────────────┐
│     维度      │   组件扫描            │   配置类 @Bean         │
├──────────────┼─────────────────────┼──────────────────────┤
│  控制粒度     │  低,自动发现          │  高,精确控制           │
│  适用对象     │  自己写的类            │  第三方库的类           │
│  创建逻辑     │  默认构造函数          │  完全自定义             │
│  代码侵入性   │  需要加注解            │  无侵入,类不用改动       │
│  集中管理     │  分散在各类中          │  集中在一个配置类         │
│  条件装配     │  @Conditional 系列    │  同样支持,更灵活         │
└──────────────┴─────────────────────┴──────────────────────┘
```
---

## 四、典型应用场景

### ✅ 用组件扫描的场景

```java
// 自己写的业务代码,直接加注解即可
@Repository
public class UserDao {
    public User find(Long id) { ... }
}

@Service
public class UserService {
    @Autowired
    private UserDao userDao;  // 自动注入
}
```
### ✅ 用 @Bean 的场景

```java
@Configuration
public class ThirdPartyConfig {

    // 第三方库的类,你无法在源码上加 @Component
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration()
              .setMatchingStrategy(MatchingStrategies.STRICT);
        return mapper;
    }

    // 需要复杂初始化逻辑的 Bean
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper om = new ObjectMapper();
        om.registerModule(new JavaTimeModule());
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return om;
    }
}
```
---

## 五、总结一句话

> - **自己写的类** → 加 `@Component`​ 系列注解,走**组件扫描**,简单省事
> - **第三方类 / 需要复杂初始化** → 用 `@Configuration`​ + `@Bean`​,走**配置类**,灵活可控
>

两者并不冲突,实际项目中通常是**混合使用**的。

‍

评论

0 条评论

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

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