依赖注入(DI)

ℹ️定位说明

Spring 系列第 2 篇。核心回答:容器怎么把依赖"塞"进对象——构造器/Setter/字段三种注入方式对比、依赖解析策略、循环依赖三级缓存(面试重灾区)、DI 核心流程伪代码。读完 IOC 篇再读本篇。

第一部分:问题的提出

一、传统方式的困境

想象一个场景:

// 传统方式:手动创建和管理依赖
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

二、理想的解决方案

我们希望能这样写:

// 理想方式:只声明依赖,框架自动注入
@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 基本用法

// 构造器注入:通过构造函数注入依赖

@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 多参数构造器注入

// 当依赖过多时,可以使用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 单元测试中的构造器注入

// 构造器注入便于单元测试

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 基本用法

// 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 基本用法

// 字段注入:通过@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 字段注入的单元测试困难

// 字段注入的单元测试困难

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 选择建议

// 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 基本用法

// 按类型匹配:默认策略

// 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 多实现的处理

// 当有多个实现时,需要指定具体实现

// 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 基本用法

// 按名称匹配:通过@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 使用建议

// 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注入

// 注入所有实现

// 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<NotificationHandler> 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注入

// 注入所有实现,以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<String, PaymentGateway> 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 循环依赖的类型

// 类型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 循环依赖的问题

// 循环依赖会导致容器启动失败

// 如果使用构造器注入,会直接报错
@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)

// Spring 使用三级缓存解决“单例 Bean 的字段注入循环依赖”

public class DefaultSingletonBeanRegistry {

    /**
     * 一级缓存:singletonObjects
     *  - 存放完全初始化完毕的单例 Bean(构造 + 依赖注入 + 初始化都完成)
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 二级缓存:earlySingletonObjects
     *  - 存放“早期暴露的 Bean 实例”(已执行构造函数,但还没依赖注入 / 初始化)
     *  - 用于解决循环依赖时,被别的 Bean 提前引用
     */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    /**
     * 三级缓存:singletonFactories
     *  - 存放可以创建“早期 Bean 实例 / 代理对象”的 ObjectFactory
     *  - 在需要时,通过工厂创建代理,并将结果放入二级缓存
     */
    private final Map<String, ObjectFactory<?>> 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)

示例场景:

@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 注入
   @Service
   public class ServiceA {
       @Autowired
       private ServiceB serviceB;
   }

   @Service
   public class ServiceB {
       @Autowired
       private ServiceA serviceA;
   }
  • 典型的“三级缓存解决循环依赖”的场景。

❌ 无法解决的情况
  1. 构造器循环依赖
   @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)作用域的循环依赖
   @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)

@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<T>

@Service
public class UserService {

    private final UserRepository userRepository;
    private final Optional<UserCache> userCache;

    public UserService(UserRepository userRepository,
                       Optional<UserCache> 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<T>

@Service
public class UserService {

    private final UserRepository userRepository;
    private final ObjectProvider<UserCache> userCacheProvider;

    public UserService(UserRepository userRepository,
                       ObjectProvider<UserCache> 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

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 示例:

cache.type=redis

14.2 @ConditionalOnClass —— 类存在才生效

@Component
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
public class RedisService {
    // 只有引入了 Jedis 依赖,这个 Bean 才会被加载
}

14.3 @ConditionalOnMissingBean —— 容器中没有某 Bean 时才创建

@Configuration
public class RepositoryConfig {

    @Bean
    @ConditionalOnMissingBean(UserRepository.class)
    public UserRepository userRepository() {
        // 当容器中没有 UserRepository 的其他实现时,才创建默认实现
        return new DefaultUserRepository();
    }
}

14.4 @Profile —— 按环境选择 Bean

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("生产环境数据源");
    }
}

配置环境:

# 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<T>ObjectProvider<T>
  4. 依赖过多要重构
    • 一个类依赖超过 4~5 个 Bean,基本就说明“职责过重”;
    • 可以提取中间层 / CommonService 来拆分。
  5. 不要在 Bean 中自己 new 其它 Bean
    • 这样会绕过 Spring 容器,失去代理 / 事务 / 生命周期管理;
    • 正确写法:通过注入依赖来使用。
  6. 少用 ApplicationContext.getBean
    • 偶尔为了兼容老代码 / 动态获取 Bean 可以用;
    • 不要当成“全局 ServiceLocator”乱用。
  7. 构造器里不要做重 IO / 大计算
    • 构造器职责:只做“装配依赖 + 校验参数”;
    • 重初始化放到 @PostConstruct 或显式的 init() 方法中。

⬅️ Spring IOC(控制反转) 🏠 00-Java ➡️ Spring AOP(面向切面编程)