[^1]: # 循环依赖:一个Spring经典坑
当你有两个 `service` 时,他们的业务互相关联,比如:<u>用户service需要查询订单service,同时订单service又需要通过用户service来查询用户</u>
这样就会形成一个闭环,如下:

## 实际代码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` 只是缓兵之计。
## 如何避免循环依赖

[^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`,走**配置类**,灵活可控
>
两者并不冲突,实际项目中通常是**混合使用**的。
暂无评论,欢迎第一个留言。
评论