--- title: "02-依赖注入(DI)" created: 2025-11-26 tags: - Java --- # 依赖注入(DI) > [!note] 定位说明 > Spring 系列第 2 篇。核心回答:**容器怎么把依赖"塞"进对象**——构造器/Setter/字段三种注入方式对比、依赖解析策略、**循环依赖三级缓存**(面试重灾区)、DI 核心流程伪代码。读完 IOC 篇再读本篇。 ## 第一部分:问题的提出 ### 一、传统方式的困境 想象一个场景: ```java // 传统方式:手动创建和管理依赖 public class OrderService { private OrderRepository orderRepository; private PaymentService paymentService; private NotificationService notificationService; public OrderService() { // 需要手动创建所有依赖 this.orderRepository = new OrderRepository(); this.paymentService = new PaymentService(); this.notificationService = new NotificationService(); } public void createOrder(Order order) { // 业务逻辑 orderRepository.save(order); paymentService.processPayment(order); notificationService.sendNotification(order); } } // 使用时也要手动创建 OrderService orderService = new OrderService(); orderService.createOrder(order); ``` 这种方式有什么问题? 1. **强耦合** - 直接依赖具体实现类,修改实现需要改动代码 2. **难以测试** - 单元测试无法mock依赖 3. **难以扩展** - 要替换实现类需要修改构造函数 4. **代码冗长** - 重复的对象创建代码 5. **依赖链复杂** - 如果OrderRepository还有其他依赖,会形成复杂的依赖链 ``` OrderService ├─ OrderRepository │ ├─ Database │ └─ Logger ├─ PaymentService │ ├─ PaymentGateway │ └─ Logger └─ NotificationService ├─ EmailService └─ Logger ``` ### 二、理想的解决方案 我们希望能这样写: ```java // 理想方式:只声明依赖,框架自动注入 @Service public class OrderService { @Autowired private OrderRepository orderRepository; @Autowired private PaymentService paymentService; @Autowired private NotificationService notificationService; public void createOrder(Order order) { // 只关注业务逻辑 orderRepository.save(order); paymentService.processPayment(order); notificationService.sendNotification(order); } } // 使用时直接注入,无需创建 @Autowired private OrderService orderService; orderService.createOrder(order); // 直接使用 ``` **这就是依赖注入要解决的问题:让框架自动管理对象的依赖关系。** --- ## 第二部分:DI的核心概念 ### 三、什么是依赖注入? #### 3.1 DI的定义 **依赖注入(Dependency Injection, DI)是一种设计模式,通过外部容器动态地将对象所依赖的其他对象注入到组件中,从而解耦组件间的依赖关系。** ``` ┌──────────────────────────────────────────┐ │ DI的本质 │ ├──────────────────────────────────────────┤ │ │ │ 传统方式: │ │ ├─ 对象自己创建依赖 │ │ ├─ 对象自己管理依赖 │ │ └─ 高度耦合 │ │ │ │ DI方式: │ │ ├─ 容器创建依赖 │ │ ├─ 容器注入依赖 │ │ └─ 解耦 │ │ │ │ 核心思想: │ │ └─ "我需要什么,不是我自己去找, │ │ 而是别人(容器)送给我" │ │ │ └──────────────────────────────────────────┘ ``` #### 3.2 DI与IOC的关系 ``` ┌──────────────────────────────────────────┐ │ DI与IOC的关系 │ ├──────────────────────────────────────────┤ │ │ │ IOC(控制反转) │ │ ├─ 是一种设计思想 │ │ ├─ 强调控制权的反转 │ │ └─ 目标:解耦、灵活、可维护 │ │ │ │ DI(依赖注入) │ │ ├─ 是实现IOC的一种方式 │ │ ├─ 强调依赖的注入 │ │ └─ 手段:通过容器注入依赖 │ │ │ │ 关系: │ │ └─ IOC是目标,DI是手段 │ │ IOC ⊃ DI │ │ │ └──────────────────────────────────────────┘ ``` #### 3.3 DI的核心优势 ``` ┌──────────────────────────────────────────┐ │ DI的核心优势 │ ├──────────────────────────────────────────┤ │ │ │ 1. 解耦 │ │ ├─ 依赖接口而不是实现 │ │ ├─ 实现类可以随意替换 │ │ └─ 修改不会影响调用方 │ │ │ │ 2. 灵活性 │ │ ├─ 通过配置选择不同实现 │ │ ├─ 支持条件装配 │ │ └─ 支持动态代理和增强 │ │ │ │ 3. 可测试性 │ │ ├─ 容易mock依赖 │ │ ├─ 支持单元测试 │ │ └─ 支持集成测试 │ │ │ │ 4. 自动化 │ │ ├─ 自动创建依赖 │ │ ├─ 自动注入依赖 │ │ └─ 自动管理生命周期 │ │ │ │ 5. 可维护性 │ │ ├─ 配置集中管理 │ │ ├─ 易于扩展 │ │ └─ 代码更清晰 │ │ │ └──────────────────────────────────────────┘ ``` --- ## 第三部分:DI的三种注入方式 ### 四、构造器注入(Constructor Injection) #### 4.1 基本用法 ```java // 构造器注入:通过构造函数注入依赖 @Service public class UserService { private final UserRepository userRepository; private final UserValidator userValidator; // Spring 4.3+ 单参构造器默认@Autowired,可省略 @Autowired public UserService(UserRepository userRepository, UserValidator userValidator) { this.userRepository = userRepository; this.userValidator = userValidator; } public void createUser(User user) { userValidator.validate(user); userRepository.save(user); } } // 使用时 @Autowired private UserService userService; userService.createUser(user); ``` #### 4.2 构造器注入的特点 ``` ┌──────────────────────────────────────────┐ │ 构造器注入的特点 │ ├──────────────────────────────────────────┤ │ │ │ 优点 │ │ ├─ 强制依赖:依赖缺失会编译错误 │ │ ├─ 不可变:final字段,线程安全 │ │ ├─ 避免循环依赖:Spring启动时检测 │ │ ├─ 易于测试:可直接传入mock对象 │ │ └─ 清晰表达:依赖关系一目了然 │ │ │ │ 缺点 │ │ ├─ 参数过多时不美观 │ │ ├─ 无法注入可选依赖 │ │ └─ 无法注入循环依赖 │ │ │ │ 推荐场景 │ │ ├─ 核心业务组件 │ │ ├─ 必须依赖 │ │ └─ 需要保证不可变性 │ │ │ └──────────────────────────────────────────┘ ``` #### 4.3 多参数构造器注入 ```java // 当依赖过多时,可以使用Lombok简化 @Service @RequiredArgsConstructor // Lombok自动生成构造器 public class OrderService { private final OrderRepository orderRepository; private final PaymentService paymentService; private final NotificationService notificationService; private final UserService userService; public void createOrder(Order order) { // 业务逻辑 } } // 等价于 @Service public class OrderService { private final OrderRepository orderRepository; private final PaymentService paymentService; private final NotificationService notificationService; private final UserService userService; public OrderService(OrderRepository orderRepository, PaymentService paymentService, NotificationService notificationService, UserService userService) { this.orderRepository = orderRepository; this.paymentService = paymentService; this.notificationService = notificationService; this.userService = userService; } } ``` #### 4.4 单元测试中的构造器注入 ```java // 构造器注入便于单元测试 public class UserServiceTest { private UserService userService; private UserRepository mockRepository; private UserValidator mockValidator; @Before public void setUp() { // 创建mock对象 mockRepository = mock(UserRepository.class); mockValidator = mock(UserValidator.class); // 通过构造器注入mock对象 userService = new UserService(mockRepository, mockValidator); } @Test public void testCreateUser() { User user = new User("Tom", 18); // 设置mock行为 when(mockValidator.validate(user)).thenReturn(true); // 执行测试 userService.createUser(user); // 验证调用 verify(mockRepository).save(user); } } ``` --- ### 五、Setter注入(Setter Injection) #### 5.1 基本用法 ```java // Setter注入:通过setter方法注入依赖 @Service public class UserService { private UserRepository userRepository; private UserValidator userValidator; // 可选依赖 @Autowired(required = false) public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } @Autowired(required = false) public void setUserValidator(UserValidator userValidator) { this.userValidator = userValidator; } public void createUser(User user) { if (userValidator != null) { userValidator.validate(user); } if (userRepository != null) { userRepository.save(user); } } } ``` #### 5.2 Setter注入的特点 ``` ┌──────────────────────────────────────────┐ │ Setter注入的特点 │ ├──────────────────────────────────────────┤ │ │ │ 优点 │ │ ├─ 灵活:支持可选依赖 │ │ ├─ 兼容:对第三方类兼容性好 │ │ ├─ 可变:可以在运行时修改依赖 │ │ └─ 简洁:代码相对简洁 │ │ │ │ 缺点 │ │ ├─ 不强约束:容器可能注入不完整 │ │ ├─ 可变性:依赖可被修改,线程不安全 │ │ ├─ 隐藏依赖:不清楚需要什么依赖 │ │ └─ 难以测试:需要手动调用setter │ │ │ │ 推荐场景 │ │ ├─ 可选依赖 │ │ ├─ 配置注入 │ │ └─ 旧代码维护 │ │ │ └──────────────────────────────────────────┘ ``` --- ### 六、字段注入(Field Injection) #### 6.1 基本用法 ```java // 字段注入:通过@Autowired注解注入依赖 @Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private UserValidator userValidator; public void createUser(User user) { userValidator.validate(user); userRepository.save(user); } } ``` #### 6.2 字段注入的特点 ``` ┌──────────────────────────────────────────┐ │ 字段注入的特点 │ ├──────────────────────────────────────────┤ │ │ │ 优点 │ │ ├─ 简洁:写法最简洁 │ │ ├─ 广泛:最常见的注入方式 │ │ └─ 快速:快速开发时方便 │ │ │ │ 缺点 │ │ ├─ 难以测试:不好mock,需要反射注入 │ │ ├─ 隐藏依赖:不清楚需要什么依赖 │ │ ├─ 可变性:依赖可被修改,线程不安全 │ │ ├─ 违背原则:违背了构造器注入的原则 │ │ └─ 不支持final:无法使用final字段 │ │ │ │ 推荐场景 │ │ ├─ 普通业务类 │ │ ├─ 快速开发 │ │ └─ 非核心组件 │ │ │ └──────────────────────────────────────────┘ ``` #### 6.3 字段注入的单元测试困难 ```java // 字段注入的单元测试困难 public class UserServiceTest { private UserService userService; @Before public void setUp() { userService = new UserService(); // ❌ 无法通过构造器注入mock对象 // 需要通过反射手动注入 UserRepository mockRepository = mock(UserRepository.class); UserValidator mockValidator = mock(UserValidator.class); // 通过反射注入(复杂且不优雅) try { Field repositoryField = UserService.class.getDeclaredField("userRepository"); repositoryField.setAccessible(true); repositoryField.set(userService, mockRepository); Field validatorField = UserService.class.getDeclaredField("userValidator"); validatorField.setAccessible(true); validatorField.set(userService, mockValidator); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } @Test public void testCreateUser() { // 测试代码 } } // ✅ 使用构造器注入则简单得多 public class UserServiceTest { private UserService userService; private UserRepository mockRepository; private UserValidator mockValidator; @Before public void setUp() { mockRepository = mock(UserRepository.class); mockValidator = mock(UserValidator.class); userService = new UserService(mockRepository, mockValidator); } } ``` --- ### 七、三种注入方式对比 #### 7.1 详细对比表 ``` ┌──────────────────────────────────────────────────────────────┐ │ 三种注入方式详细对比 │ ├──────────────────────────────────────────────────────────────┤ │ │ │ 维度 构造器注入 Setter注入 字段注入 │ │ ───────────────────────────────────────────────────────── │ │ 依赖清晰度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ │ │ 不可变性 ⭐⭐⭐⭐⭐ ⭐ ⭐ │ │ 测试友好度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐ │ │ 灵活性 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ │ │ 可选依赖 ❌ ✅ ✅ │ │ 循环依赖 ✅ ✅ ❌ │ │ 代码简洁度 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ │ │ 推荐度 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐ │ │ │ └──────────────────────────────────────────────────────────────┘ ``` #### 7.2 选择建议 ```java // 1. 优先使用构造器注入 @Service public class OrderService { private final OrderRepository orderRepository; private final PaymentService paymentService; public OrderService(OrderRepository orderRepository, PaymentService paymentService) { this.orderRepository = orderRepository; this.paymentService = paymentService; } } // 2. 可选依赖使用Setter注入 @Service public class UserService { private UserRepository userRepository; private UserCache userCache; // 可选 @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } @Autowired(required = false) public void setUserCache(UserCache userCache) { this.userCache = userCache; } } // 3. 快速开发时可使用字段注入 @Service public class CustomerService { @Autowired private NotificationService notificationService; } // 4. 避免混用,保持一致性 // ❌ 不要混用多种注入方式 @Service public class BadService { @Autowired private UserRepository userRepository; // 字段注入 private PaymentService paymentService; @Autowired public void setPaymentService(PaymentService paymentService) { // Setter注入 this.paymentService = paymentService; } } // ✅ 应该统一使用一种方式 @Service public class GoodService { private final UserRepository userRepository; private final PaymentService paymentService; public GoodService(UserRepository userRepository, PaymentService paymentService) { this.userRepository = userRepository; this.paymentService = paymentService; } } ``` --- ## 第四部分:依赖解析策略 ### 八、按类型匹配(ByType) #### 8.1 基本用法 ```java // 按类型匹配:默认策略 // 1. 定义接口 public interface UserRepository { User findById(int id); } // 2. 定义实现 @Repository public class MysqlUserRepository implements UserRepository { @Override public User findById(int id) { return new User(id, "Tom"); } } // 3. 注入时按类型匹配 @Service public class UserService { @Autowired private UserRepository userRepository; // 按UserRepository类型查找 public User getUser(int id) { return userRepository.findById(id); } } // Spring会自动找到MysqlUserRepository的实现并注入 ``` #### 8.2 多实现的处理 ```java // 当有多个实现时,需要指定具体实现 // 1. 定义接口 public interface PaymentGateway { void processPayment(Order order); } // 2. 定义多个实现 @Component("alipayGateway") public class AlipayGateway implements PaymentGateway { @Override public void processPayment(Order order) { System.out.println("支付宝支付"); } } @Component("wechatGateway") public class WechatGateway implements PaymentGateway { @Override public void processPayment(Order order) { System.out.println("微信支付"); } } // 3. 使用@Qualifier指定具体实现 @Service public class OrderService { @Autowired @Qualifier("alipayGateway") private PaymentGateway paymentGateway; public void createOrder(Order order) { paymentGateway.processPayment(order); } } ``` --- ### 九、按名称匹配(ByName) #### 9.1 基本用法 ```java // 按名称匹配:通过@Qualifier或@Resource // 1. 定义Bean @Component("mysqlRepository") public class MysqlUserRepository implements UserRepository { } @Component("mongoRepository") public class MongoUserRepository implements UserRepository { } // 2. 按名称注入 @Service public class UserService { // 方式1:@Autowired + @Qualifier @Autowired @Qualifier("mysqlRepository") private UserRepository userRepository; // 方式2:@Resource(name = "...") @Resource(name = "mysqlRepository") private UserRepository userRepository2; } ``` #### 9.2 @Autowired vs @Resource ``` ┌──────────────────────────────────────────┐ │ @Autowired vs @Resource │ ├──────────────────────────────────────────┤ │ │ │ 维度 @Autowired @Resource │ │ ───────────────────────────────────── │ │ 来源 Spring Java标准 │ │ 默认匹配方式 ByType ByName │ │ 支持@Qualifier ✅ ❌ │ │ required属性 ✅ ❌ │ │ 可移植性 ❌ ✅ │ │ IDE推荐 ❌ ✅ │ │ │ └──────────────────────────────────────────┘ ``` #### 9.3 使用建议 ```java // 1. 优先使用@Autowired + @Qualifier @Service public class UserService { @Autowired @Qualifier("mysqlRepository") private UserRepository userRepository; } // 2. 需要按名称匹配时使用@Resource @Service public class UserService { @Resource(name = "mysqlRepository") private UserRepository userRepository; } // 3. 可选依赖使用@Autowired(required = false) @Service public class UserService { @Autowired(required = false) private UserCache userCache; } // 4. 避免使用@Resource的name属性 // ❌ 不推荐 @Resource(name = "userRepository") private UserRepository userRepository; // ✅ 推荐 @Autowired @Qualifier("userRepository") private UserRepository userRepository; ``` --- ### 十、集合注入 #### 10.1 List注入 ```java // 注入所有实现 // 1. 定义接口 public interface NotificationHandler { void handle(Notification notification); } // 2. 定义多个实现 @Component public class EmailNotificationHandler implements NotificationHandler { @Override public void handle(Notification notification) { System.out.println("发送邮件"); } } @Component public class SmsNotificationHandler implements NotificationHandler { @Override public void handle(Notification notification) { System.out.println("发送短信"); } } @Component public class PushNotificationHandler implements NotificationHandler { @Override public void handle(Notification notification) { System.out.println("发送推送"); } } // 3. 注入所有实现 @Service public class NotificationService { @Autowired private List handlers; public void notify(Notification notification) { // 遍历所有处理器 for (NotificationHandler handler : handlers) { handler.handle(notification); } } } // 4. 控制顺序 @Component @Order(1) public class EmailNotificationHandler implements NotificationHandler { } @Component @Order(2) public class SmsNotificationHandler implements NotificationHandler { } @Component @Order(3) public class PushNotificationHandler implements NotificationHandler { } // 注入时会按@Order排序 ``` #### 10.2 Map注入 ```java // 注入所有实现,以Bean名称为key // 1. 定义接口 public interface PaymentGateway { void processPayment(Order order); } // 2. 定义多个实现 @Component("alipay") public class AlipayGateway implements PaymentGateway { @Override public void processPayment(Order order) { System.out.println("支付宝支付"); } } @Component("wechat") public class WechatGateway implements PaymentGateway { @Override public void processPayment(Order order) { System.out.println("微信支付"); } } // 3. 注入为Map @Service public class PaymentService { @Autowired private Map gateways; public void processPayment(Order order, String paymentMethod) { PaymentGateway gateway = gateways.get(paymentMethod); if (gateway != null) { gateway.processPayment(order); } } } // 4. 使用 PaymentService paymentService = applicationContext.getBean(PaymentService.class); paymentService.processPayment(order, "alipay"); // 支付宝支付 paymentService.processPayment(order, "wechat"); // 微信支付 ``` --- ## 第五部分:循环依赖处理 ### 十一、什么是循环依赖? #### 11.1 循环依赖的类型 ```java // 类型1:直接循环依赖 @Service public class ServiceA { @Autowired private ServiceB serviceB; // A依赖B } @Service public class ServiceB { @Autowired private ServiceA serviceA; // B依赖A } // 类型2:间接循环依赖 @Service public class ServiceA { @Autowired private ServiceB serviceB; // A→B } @Service public class ServiceB { @Autowired private ServiceC serviceC; // B→C } @Service public class ServiceC { @Autowired private ServiceA serviceA; // C→A } // 类型3:自身依赖 @Service public class ServiceA { @Autowired private ServiceA serviceA; // 自己依赖自己 } ``` #### 11.2 循环依赖的问题 ```java // 循环依赖会导致容器启动失败 // 如果使用构造器注入,会直接报错 @Service public class ServiceA { public ServiceA(ServiceB serviceB) { // 构造器注入 this.serviceB = serviceB; } } @Service public class ServiceB { public ServiceB(ServiceA serviceA) { // 构造器注入 this.serviceA = serviceA; } } // 启动时报错: // BeanCurrentlyInCreationException: Error creating bean with name 'serviceA' ``` ### 十二、Spring 三级缓存与单例循环依赖 #### 12.1 三级缓存结构(简化版 DefaultSingletonBeanRegistry) ```java // Spring 使用三级缓存解决“单例 Bean 的字段注入循环依赖” public class DefaultSingletonBeanRegistry { /** * 一级缓存:singletonObjects * - 存放完全初始化完毕的单例 Bean(构造 + 依赖注入 + 初始化都完成) */ private final Map singletonObjects = new ConcurrentHashMap<>(); /** * 二级缓存:earlySingletonObjects * - 存放“早期暴露的 Bean 实例”(已执行构造函数,但还没依赖注入 / 初始化) * - 用于解决循环依赖时,被别的 Bean 提前引用 */ private final Map earlySingletonObjects = new ConcurrentHashMap<>(); /** * 三级缓存:singletonFactories * - 存放可以创建“早期 Bean 实例 / 代理对象”的 ObjectFactory * - 在需要时,通过工厂创建代理,并将结果放入二级缓存 */ private final Map> singletonFactories = new ConcurrentHashMap<>(); /** * 获取单例 Bean 的核心流程 */ public Object getSingleton(String beanName) { // 1. 先从一级缓存中查找:完整 Bean Object singletonObject = singletonObjects.get(beanName); if (singletonObject != null) { return singletonObject; } // 2. 再从二级缓存中查找:早期暴露的 Bean(半成品) singletonObject = earlySingletonObjects.get(beanName); if (singletonObject != null) { return singletonObject; } // 3. 最后从三级缓存中查找:Bean 工厂 ObjectFactory singletonFactory = singletonFactories.get(beanName); if (singletonFactory != null) { // 通过工厂创建 Bean(注意:有可能创建的是代理对象) singletonObject = singletonFactory.getObject(); // 将创建好的 Bean 放到二级缓存中(早期引用用它) earlySingletonObjects.put(beanName, singletonObject); // 同时从三级缓存中移除对应工厂 singletonFactories.remove(beanName); } return singletonObject; } /** * 单例 Bean 创建完成后,放入一级缓存 */ public void addSingleton(String beanName, Object singletonObject) { singletonObjects.put(beanName, singletonObject); earlySingletonObjects.remove(beanName); singletonFactories.remove(beanName); } /** * Bean 构造完成后(还未依赖注入 / 初始化),注册对应的 ObjectFactory * 用于后续“提前暴露” */ public void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { if (!singletonObjects.containsKey(beanName)) { singletonFactories.put(beanName, singletonFactory); earlySingletonObjects.remove(beanName); } } } ``` --- #### 12.2 字段注入循环依赖的解决流程(ServiceA ↔ ServiceB) 示例场景: ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; } @Service public class ServiceB { @Autowired private ServiceA serviceA; } ``` **前提:** - 两个都是 `singleton`(默认) - 使用的是 **字段注入 / setter 注入**(不是构造器注入) **流程(简化版):** 1. **创建 ServiceA** - 调用构造器,实例化 `ServiceA` - **将** `ServiceA` **对应的 ObjectFactory 放入三级缓存**(`singletonFactories`) - 此时: - 一级:无 - 二级:无 - 三级:`factory(A)` 2. **为 ServiceA 注入依赖 → 发现需要 ServiceB** - 容器开始创建 `ServiceB` - 实例化 `ServiceB`,调用构造器 - 将 `ServiceB` 对应的 ObjectFactory 放入三级缓存 - 此时可能会把 `ServiceB` 的“早期引用”放入二级缓存(不同版本源码略有差异) 3. **为 ServiceB 注入依赖 → 发现需要 ServiceA** - `getBean("serviceA")` - 按顺序查找: 1. 一级缓存:没有 2. 二级缓存:没有 3. 三级缓存:找到了 `factory(A)` - 调用 `factory(A).getObject()` 创建 **早期的 A(可能是代理)**: - 将该 A 放入 **二级缓存** `earlySingletonObjects` - 从三级缓存移除 A 的工厂 - 此时: - 一级:无 - 二级:`A(半成品)` - 三级:`factory(B)` 4. **把“早期的 A” 注入到 B 中** - 此时 `serviceB.serviceA = earlyA` - 完成 `ServiceB` 的依赖注入和初始化 - 将 `ServiceB` 放入 **一级缓存** - 清理它在二级 / 三级中的痕迹 5. **回到 ServiceA,继续注入它的依赖** - 现在 `ServiceB` 已经是完整 Bean,在 **一级缓存** - `ServiceA` 再次获取 `ServiceB` → 从一级缓存中直接拿 - 为 `ServiceA` 完成依赖注入和初始化 - 将 `ServiceA` 放入一级缓存,清理二级缓存中的早期引用 6. **最终结果** - 一级缓存:`A(完整)`, `B(完整)` - 二级 / 三级缓存:不再存放 A/B - **循环依赖被成功打破** --- #### 12.3 三个缓存的作用小结 - **一级缓存 singletonObjects** - 完整、可直接使用的单例 Bean - 绝大部分情况下,从这里直接返回 - **二级缓存 earlySingletonObjects** - 早期暴露的 Bean 实例(已构造,但尚未完成依赖注入 / 初始化) - 典型用途:**被其它单例在循环依赖场景中提前引用** - **三级缓存 singletonFactories** - 存放 `ObjectFactory`,可以延迟创建 Bean 或代理 - 在需要“提前暴露”某个 Bean 时,通过工厂创建早期引用,并将结果升级到二级缓存 --- #### 12.4 哪些循环依赖可以解决 / 不能解决? ##### ✅ 可以解决的情况 1. **单例 + 字段 / setter 注入** ```java @Service public class ServiceA { @Autowired private ServiceB serviceB; } @Service public class ServiceB { @Autowired private ServiceA serviceA; } ``` - 典型的“三级缓存解决循环依赖”的场景。 --- ##### ❌ 无法解决的情况 1. **构造器循环依赖** ```java @Service public class ServiceA { private final ServiceB serviceB; public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; } } @Service public class ServiceB { private final ServiceA serviceA; public ServiceB(ServiceA serviceA) { this.serviceA = serviceA; } } ``` - 两个 Bean 在**构造阶段就互相需要**,此时还没机会把任何一个“早期暴露”出去。 - Spring 会抛出:`BeanCurrentlyInCreationException`。 1. **原型(prototype)作用域的循环依赖** ```java @Service @Scope("prototype") public class ServiceA { @Autowired private ServiceB serviceB; } @Service @Scope("prototype") public class ServiceB { @Autowired private ServiceA serviceA; } ``` - 原型 Bean **每次 getBean 都要重新创建**,不会进单例缓存。 - Spring 不会为原型 Bean 构建完整的三级缓存解决链,也就无法用之前的机制打破循环。 - 同样会抛出 `BeanCurrentlyInCreationException`。 --- ### 十三、可选依赖:required = false / Optional / ObjectProvider #### 13.1 `@Autowired(required = false)` ```java @Service public class UserService { // 必须存在的依赖 @Autowired private UserRepository userRepository; // 可选依赖:如果容器中有 UserCache 就注入,没有就保持 null @Autowired(required = false) private UserCache userCache; public User getUser(int id) { if (userCache != null) { User cached = userCache.get(id); if (cached != null) { return cached; } } User user = userRepository.findById(id); if (userCache != null) { userCache.put(id, user); } return user; } } ``` --- #### 13.2 使用 `Optional` ```java @Service public class UserService { private final UserRepository userRepository; private final Optional userCache; public UserService(UserRepository userRepository, Optional userCache) { this.userRepository = userRepository; this.userCache = userCache; } public User getUser(int id) { return userCache .map(cache -> { User cached = cache.get(id); return cached != null ? cached : userRepository.findById(id); }) .orElseGet(() -> userRepository.findById(id)); } } ``` --- #### 13.3 使用 `ObjectProvider` ```java @Service public class UserService { private final UserRepository userRepository; private final ObjectProvider userCacheProvider; public UserService(UserRepository userRepository, ObjectProvider userCacheProvider) { this.userRepository = userRepository; this.userCacheProvider = userCacheProvider; } public User getUser(int id) { // 延迟获取可选 Bean UserCache userCache = userCacheProvider.getIfAvailable(); if (userCache != null) { User cached = userCache.get(id); if (cached != null) { return cached; } } return userRepository.findById(id); } } ``` --- ### 十四、条件注入:`@Conditional*` 与 `@Profile` #### 14.1 `@ConditionalOnProperty` —— 根据配置决定 Bean ```java public interface CacheManager { void put(String key, Object value); Object get(String key); } @Component @ConditionalOnProperty(name = "cache.type", havingValue = "redis") public class RedisCacheManager implements CacheManager { @Override public void put(String key, Object value) { System.out.println("Redis 缓存写入"); } @Override public Object get(String key) { return null; } } @Component @ConditionalOnProperty(name = "cache.type", havingValue = "memory", matchIfMissing = true) public class MemoryCacheManager implements CacheManager { @Override public void put(String key, Object value) { System.out.println("内存缓存写入"); } @Override public Object get(String key) { return null; } } ``` `application.properties` 示例: ```properties cache.type=redis ``` --- #### 14.2 `@ConditionalOnClass` —— 类存在才生效 ```java @Component @ConditionalOnClass(name = "redis.clients.jedis.Jedis") public class RedisService { // 只有引入了 Jedis 依赖,这个 Bean 才会被加载 } ``` --- #### 14.3 `@ConditionalOnMissingBean` —— 容器中没有某 Bean 时才创建 ```java @Configuration public class RepositoryConfig { @Bean @ConditionalOnMissingBean(UserRepository.class) public UserRepository userRepository() { // 当容器中没有 UserRepository 的其他实现时,才创建默认实现 return new DefaultUserRepository(); } } ``` --- #### 14.4 `@Profile` —— 按环境选择 Bean ```java public interface DataSource { void connect(); } @Component @Profile("dev") public class DevDataSource implements DataSource { @Override public void connect() { System.out.println("开发环境数据源"); } } @Component @Profile("prod") public class ProdDataSource implements DataSource { @Override public void connect() { System.out.println("生产环境数据源"); } } ``` 配置环境: ```properties # application.properties spring.profiles.active=dev ``` --- ### 十五、依赖注入(DI)的核心流程(Spring 大致做了什么) > 伪代码 / 思路版,方便你面试时脑中有“完整链路”。 1. **扫描配置** - 扫描 `@Component`, `@Service`, `@Repository`, `@Controller` 等,生成 `BeanDefinition`。 2. **创建 Bean 实例** - 调用构造函数实例化对象(构造器注入在这个阶段完成)。 3. **填充属性(依赖注入)** - 解析字段 / setter 上的 `@Autowired` / `@Resource` / `@Value` 等; - 利用三级缓存 / BeanFactory 递归获取依赖 Bean,并注入。 4. **调用 BeanPostProcessor(前置)** - 如:`AutowiredAnnotationBeanPostProcessor`、AOP 相关的 `AutoProxyCreator` 等。 5. **调用初始化方法** - `@PostConstruct` - `InitializingBean.afterPropertiesSet()` - 自定义 `init-method` 6. **调用 BeanPostProcessor(后置)** - 对 Bean 做一些代理增强(AOP)、包装等。 7. **放入单例池** - 对于单例 Bean,将其放入 **一级缓存** `singletonObjects`,供以后直接复用。 --- ### 十六、DI 的推荐实践 1. **优先使用构造器注入** - 可以配合 Lombok:`@RequiredArgsConstructor` - 好处:依赖是 `final`,更容易做单元测试,也能避免循环依赖时“悄悄注不进去还不知道”。 2. **减少字段注入** - 字段注入只能靠反射赋值,不利于测试和重构; - 可以保留少量字段注入给框架用(比如在配置类里)。 3. **可选依赖用 Optional / ObjectProvider** - 不推荐满世界 `required = false`; - 推荐: - 强业务依赖 → 构造器必选参数 - 可选功能 / 插件式依赖 → `Optional` 或 `ObjectProvider`。 4. **依赖过多要重构** - 一个类依赖超过 4~5 个 Bean,基本就说明“职责过重”; - 可以提取中间层 / CommonService 来拆分。 5. **不要在 Bean 中自己** `new` **其它 Bean** - 这样会绕过 Spring 容器,失去代理 / 事务 / 生命周期管理; - 正确写法:通过注入依赖来使用。 6. **少用 ApplicationContext.getBean** - 偶尔为了兼容老代码 / 动态获取 Bean 可以用; - 不要当成“全局 ServiceLocator”乱用。 7. **构造器里不要做重 IO / 大计算** - 构造器职责:只做“装配依赖 + 校验参数”; - 重初始化放到 `@PostConstruct` 或显式的 `init()` 方法中。 --- ⬅️ [[01-Spring IOC(控制反转)|Spring IOC(控制反转)]] 🏠 [[00-Java|00-Java]] ➡️ [[03-Spring AOP(面向切面编程)|Spring AOP(面向切面编程)]]