Spring 核心原理:IoC/DI 与 Bean 生命周期全景解析 作为 Java 后端开发者Spring 几乎是我们职业生涯中绕不开的框架。但很多人用了很多年 Spring每天写着Service、Autowired、Bean却始终没有真正搞懂它的核心到底什么是控制反转它和依赖注入是什么关系Spring 是如何创建和管理 Bean 的一个 Bean 从出生到死亡经历了什么为什么在构造方法中使用Autowired注入的依赖会是 null如何在 Bean 初始化前后插入自定义逻辑这些问题不仅是日常开发中排查问题的关键更是面试中 100% 会被深挖的核心考点。很多人能背出 Bean 生命周期的几个步骤却不知道每个步骤的作用能写出依赖注入的代码却不理解背后的设计思想。这篇文章我会用最通俗易懂的语言从核心思想→实现机制→底层流程→实战应用四个维度把 Spring 的这两大核心概念讲透。看完这篇你不仅能轻松应对所有相关面试题更能从根本上理解 Spring 的设计哲学写出更优雅、更健壮的代码。一、先理清两大核心概念的关系在开始之前我们先把这两个概念的关系理清楚建立一个整体的认知框架控制反转IoC是 Spring 的核心设计思想它反转了对象创建和依赖管理的控制权依赖注入DI是控制反转的具体实现方式Spring 通过 DI 来实现 IoCBean 生命周期是 Spring 管理 Bean 的具体执行过程从 Bean 创建到销毁的完整流程简单来说IoC 是思想DI 是实现Bean 生命周期是执行过程。这三个概念层层递进共同构成了 Spring IoC 容器的核心骨架。二、控制反转IoCSpring 的灵魂1. 什么是控制反转控制反转全称 Inversion of Control简称 IoC。它不是一种技术而是一种设计思想其核心是将对象创建和依赖管理的控制权从开发者手中转移到 Spring 容器中。在传统的开发模式中对象的创建和依赖管理的控制权在开发者自己手里。如果一个对象 A 依赖对象 B我们需要在 A 的代码中手动new B()来创建依赖对象。这种方式的问题非常明显代码耦合度高、难以测试、代码冗余。而在 IoC 模式中我们不需要手动创建对象只需要声明依赖关系Spring 容器会自动创建对象并注入依赖。这样对象之间的耦合度大大降低代码也更加灵活和可测试。2. 一个例子看懂 IoC 的优势我们用最经典的 用户服务依赖订单服务 的例子来对比两种模式的区别传统开发模式主动控制// 订单服务 public class OrderService { public void createOrder() { System.out.println(创建订单); } } // 用户服务 public class UserService { // 手动创建依赖对象硬编码耦合 private OrderService orderService new OrderService(); public void createUser() { System.out.println(创建用户); orderService.createOrder(); } }这种模式的致命问题耦合度极高如果要替换OrderService的实现必须修改UserService的代码无法单元测试无法将OrderService替换为模拟对象测试必须依赖真实的OrderService代码冗余每个需要依赖的地方都要手动创建对象IoC 模式被动接收// 订单服务交给Spring容器管理 Service public class OrderService { public void createOrder() { System.out.println(创建订单); } } // 用户服务交给Spring容器管理 Service public class UserService { // 声明依赖Spring自动注入 Autowired private OrderService orderService; public void createUser() { System.out.println(创建用户); orderService.createOrder(); } }在 IoC 模式中UserService不需要手动创建OrderService只需要声明依赖Spring 容器会自动创建OrderService对象并注入到UserService中如果要替换OrderService的实现只需要修改OrderService的注解不需要修改UserService的代码单元测试时可以轻松地将OrderService替换为模拟对象3. IoC 的核心IoC 容器IoC 的核心是IoC 容器。Spring 容器负责创建对象、管理对象的生命周期、注入依赖关系。它就像一个工厂我们只需要告诉它需要什么对象它就会为我们生产并组装好这些对象。Spring 容器的工作流程可以概括为三步加载配置读取 XML 配置、注解或 Java 配置获取 Bean 的定义信息创建 Bean根据 Bean 定义信息通过反射创建 Bean 实例注入依赖解析 Bean 之间的依赖关系将依赖注入到对应的 Bean 中4. BeanDefinitionBean 的蓝图Spring 容器是如何知道要创建哪些 Bean、如何创建 Bean 的呢答案是BeanDefinition。BeanDefinition 是 Bean 的 蓝图它包含了 Bean 的所有定义信息Bean 的类名Bean 的作用域单例、原型等Bean 的依赖关系Bean 的初始化方法和销毁方法其他配置信息当 Spring 启动时它会扫描所有的配置Component、Service、Bean等将每个 Bean 的信息封装成一个BeanDefinition对象存储在 IoC 容器中。然后根据这些BeanDefinition来创建和管理 Bean。三、依赖注入DIIoC 的实现方式依赖注入全称 Dependency Injection简称 DI。它是控制反转的具体实现方式。简单来说依赖注入就是 Spring 容器在创建 Bean 时自动将它依赖的其他 Bean 注入到当前 Bean 中。1. 三种依赖注入方式详解Spring 提供了三种依赖注入方式分别是构造方法注入、setter 方法注入和字段注入。每种方式都有自己的优缺点和适用场景。1构造方法注入Spring 官方推荐通过构造方法注入依赖Spring 会在实例化 Bean 时调用构造方法并传入依赖对象。Service public class UserService { // 可以声明为final保证依赖不可变 private final OrderService orderService; // Spring 4.3以后如果类只有一个构造方法可以省略Autowired注解 public UserService(OrderService orderService) { this.orderService orderService; } }优点✅ 可以注入final字段保证依赖不可变✅ 依赖关系明确所有依赖都在构造方法中声明✅ 避免循环依赖问题✅ 单元测试时不需要启动 Spring 容器可以手动创建依赖对象缺点当依赖较多时构造方法会变得很长2setter 方法注入通过 setter 方法注入依赖Spring 会在实例化 Bean 之后调用 setter 方法注入依赖。Service public class UserService { private OrderService orderService; Autowired public void setOrderService(OrderService orderService) { this.orderService orderService; } }优点✅ 支持可选依赖可以在运行时动态修改依赖✅ 不会导致循环依赖问题缺点❌ 依赖可以被修改可能导致不可预期的问题❌ 依赖关系不明确需要查看所有 setter 方法才能知道依赖了哪些对象3字段注入不推荐通过在字段上添加Autowired注解注入依赖这是最常用也是最简单的方式但 Spring 官方并不推荐。Service public class UserService { Autowired private OrderService orderService; }优点代码简洁使用方便缺点❌ 无法注入final字段❌ 容易导致循环依赖问题❌ 依赖关系不明显不利于代码可读性❌ 单元测试时需要启动 Spring 容器否则无法注入依赖2. 三种注入方式对比与最佳实践注入方式代码简洁性依赖不可变性循环依赖单元测试推荐指数构造方法注入⭐⭐⭐✅❌✅⭐⭐⭐⭐⭐setter 方法注入⭐⭐⭐❌❌⭐⭐⭐⭐⭐字段注入⭐⭐⭐⭐⭐❌✅但不推荐❌⭐⭐最佳实践优先使用构造方法注入这是 Spring 官方推荐的方式具有依赖不可变、依赖关系明确、便于单元测试等优点对于可选依赖使用 setter 方法注入如果依赖是可选的可以使用 setter 方法注入尽量避免使用字段注入虽然字段注入代码简洁但它有很多缺点不推荐在生产环境中使用3.AutowiredvsResource很多人搞不清这两个注解的区别它们都是用于依赖注入的但有以下本质不同特性AutowiredResource来源Spring 框架JSR-250 标准Java 官方注入方式默认按类型注入默认按名称注入支持的注入点字段、setter 方法、构造方法字段、setter 方法必须性默认必须存在否则抛出异常默认必须存在否则抛出异常使用建议如果是 Spring 项目优先使用Autowired如果需要按名称注入可以使用Autowired Qualifier或者直接使用Resource4. 常见问题循环依赖问题描述两个 Bean 相互依赖比如 A 依赖 BB 依赖 A导致 Spring 无法完成依赖注入。解决方案✅ 使用构造方法注入可以避免大部分循环依赖问题如果必须使用字段注入可以使用Lazy注解延迟加载依赖最根本的解决方案是优化代码结构避免循环依赖四、Bean 生命周期Spring 如何管理 BeanBean 的生命周期指的是一个 Bean 从被创建到被销毁的整个过程。在 Spring 中Bean 的生命周期完全由 IoC 容器管理我们不需要手动创建和销毁对象。1. 四个核心阶段任何 Bean 的生命周期都可以分为四个核心阶段按顺序执行实例化Spring 通过反射调用 Bean 的构造方法创建一个空的对象依赖注入Spring 解析 Bean 的依赖关系将依赖的 Bean 注入到当前 Bean 中初始化执行 Bean 的初始化逻辑完成自定义的初始化操作销毁当 Spring 容器关闭时执行 Bean 的销毁方法释放资源2. 完整生命周期13 步在四个核心阶段的基础上Spring 提供了大量的扩展点允许我们在 Bean 生命周期的不同时刻插入自定义逻辑。完整的生命周期分为 13 个步骤阶段 1容器启动加载 BeanDefinitionSpring 读取配置将 Bean 的定义信息封装成BeanDefinition加载到容器中阶段 2Bean 实例化实例化 BeanSpring 通过反射调用 Bean 的构造方法创建一个空的对象InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()在实例化之前执行可以返回一个代理对象替代原 BeanInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()在实例化之后执行可以修改 Bean 的属性阶段 3依赖注入依赖注入Spring 为 Bean 注入它所依赖的其他 BeanInstantiationAwareBeanPostProcessor.postProcessProperties()在依赖注入之后执行可以修改 Bean 的属性值阶段 4初始化Aware 接口注入Spring 为实现了 Aware 接口的 Bean 注入对应的资源比如BeanNameAware注入 Bean 的名称BeanFactoryAware注入BeanFactoryApplicationContextAware注入ApplicationContextBeanPostProcessor.postProcessBeforeInitialization()在初始化方法执行之前执行初始化方法执行按以下顺序执行三个初始化方法PostConstruct注解标注的方法实现InitializingBean接口的afterPropertiesSet()方法XML 或Bean注解指定的initMethod方法BeanPostProcessor.postProcessAfterInitialization()在初始化方法执行之后执行这是 AOP 代理生成的地方阶段 5Bean 使用Bean 就绪Bean 已经完全初始化可以被使用了阶段 6容器关闭销毁方法执行按以下顺序执行三个销毁方法PreDestroy注解标注的方法实现DisposableBean接口的destroy()方法XML 或Bean注解指定的destroyMethod方法3. 生命周期流程图为了方便大家记忆我整理了一张清晰的生命周期流程图4. 三个最重要的扩展点Spring 提供了很多扩展点但这三个是最常用也是面试最常考的1BeanPostProcessorBean 级别的后置处理器在每个 Bean的初始化前后执行。使用场景对所有 Bean 进行统一处理比如属性注入、代理生成、参数校验等。代码示例Component public class MyBeanPostProcessor implements BeanPostProcessor { Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(初始化前 beanName); return bean; } Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(初始化后 beanName); // 这里可以生成AOP代理 return bean; } }2BeanFactoryPostProcessor容器级别的后置处理器在所有 BeanDefinition 加载完成之后Bean 实例化之前执行。使用场景修改BeanDefinition的属性比如修改 Bean 的作用域、替换 Bean 的实现类等。代码示例Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition beanDefinition beanFactory.getBeanDefinition(userService); // 修改Bean的作用域为原型 beanDefinition.setScope(prototype); } }3InstantiationAwareBeanPostProcessorBeanPostProcessor的子接口在Bean 实例化前后执行。使用场景在实例化之前返回一个代理对象替代原 Bean 的实例化。5. 实战自定义 Bean 生命周期回调下面我们通过一个完整的例子演示如何自定义 Bean 的生命周期回调Service public class UserService implements InitializingBean, DisposableBean, BeanNameAware { private String beanName; // 构造方法 public UserService() { System.out.println(1. 执行构造方法实例化Bean); } // BeanNameAware接口方法 Override public void setBeanName(String name) { this.beanName name; System.out.println(2. 执行BeanNameAware.setBeanName()注入Bean名称 name); } // PostConstruct注解标注的初始化方法 PostConstruct public void postConstruct() { System.out.println(3. 执行PostConstruct标注的初始化方法); } // InitializingBean接口方法 Override public void afterPropertiesSet() throws Exception { System.out.println(4. 执行InitializingBean.afterPropertiesSet()方法); } // Bean注解指定的初始化方法 public void initMethod() { System.out.println(5. 执行Bean指定的initMethod方法); } // 业务方法 public void createUser() { System.out.println(6. 执行业务方法createUser()); } // PreDestroy注解标注的销毁方法 PreDestroy public void preDestroy() { System.out.println(7. 执行PreDestroy标注的销毁方法); } // DisposableBean接口方法 Override public void destroy() throws Exception { System.out.println(8. 执行DisposableBean.destroy()方法); } // Bean注解指定的销毁方法 public void destroyMethod() { System.out.println(9. 执行Bean指定的destroyMethod方法); } }启动 Spring 容器运行结果如下1. 执行构造方法实例化Bean 2. 执行BeanNameAware.setBeanName()注入Bean名称userService 3. 执行PostConstruct标注的初始化方法 4. 执行InitializingBean.afterPropertiesSet()方法 5. 执行Bean指定的initMethod方法 6. 执行业务方法createUser() 7. 执行PreDestroy标注的销毁方法 8. 执行DisposableBean.destroy()方法 9. 执行Bean指定的destroyMethod方法可以看到生命周期的执行顺序和我们之前讲的完全一致。可以看到生命周期的执行顺序和我们之前讲的完全一致。五、常见坑与避坑指南1. IoC/DI 常见坑坑 1在构造方法中使用 Autowired 注入的依赖问题在构造方法中使用Autowired注入的依赖会是 null因为构造方法执行时依赖注入还没有完成。错误示例Service public class UserService { Autowired private OrderService orderService; public UserService() { // 这里orderService是null会抛出空指针异常 orderService.createOrder(); } }解决方案使用构造方法注入或者在PostConstruct方法中使用依赖。正确示例Service public class UserService { private final OrderService orderService; // 构造方法注入 public UserService(OrderService orderService) { this.orderService orderService; // 这里可以安全地使用orderService orderService.createOrder(); } }坑 2字段注入导致的空指针异常问题在某些情况下比如手动创建对象字段注入的依赖会是 null导致空指针异常。解决方案优先使用构造方法注入避免字段注入。2. Bean 生命周期常见坑坑 1初始化方法的执行顺序混乱问题同时使用PostConstruct、InitializingBean和initMethod不清楚它们的执行顺序。解决方案记住执行顺序PostConstruct→InitializingBean.afterPropertiesSet()→initMethod。推荐使用PostConstruct注解最简洁也最符合 Java 规范。坑 2BeanPostProcessor 中注入依赖导致循环依赖问题在BeanPostProcessor中注入其他 Bean可能会导致循环依赖问题。解决方案尽量不要在BeanPostProcessor中注入其他 Bean如果必须注入使用Lazy注解延迟注入。六、高频面试题解答问什么是控制反转什么是依赖注入它们有什么关系答控制反转是一种设计思想它反转了对象创建和依赖管理的控制权从开发者手中转移到了 Spring 容器中。依赖注入是控制反转的具体实现方式Spring 通过依赖注入来实现控制反转。问依赖注入有哪几种方式各自的优缺点是什么答依赖注入有三种方式构造方法注入、setter 方法注入和字段注入。构造方法注入是 Spring 官方推荐的方式具有依赖不可变、依赖关系明确、便于单元测试等优点setter 方法注入适合可选依赖字段注入虽然代码简洁但有很多缺点不推荐使用。问Spring Bean 的完整生命周期是什么答Spring Bean 的生命周期分为四个核心阶段实例化、依赖注入、初始化、销毁。完整流程包括 13 个步骤加载 BeanDefinition、实例化 Bean、依赖注入、Aware 接口注入、BeanPostProcessor 前置处理、初始化方法执行、BeanPostProcessor 后置处理、Bean 就绪、容器关闭、销毁方法执行。问BeanPostProcessor 和 BeanFactoryPostProcessor 有什么区别答BeanPostProcessor 是 Bean 级别的后置处理器在每个 Bean 的初始化前后执行BeanFactoryPostProcessor 是容器级别的后置处理器在所有 BeanDefinition 加载完成之后Bean 实例化之前执行。问AOP 代理是在 Bean 生命周期的哪个阶段生成的答AOP 代理是在BeanPostProcessor.postProcessAfterInitialization()方法中生成的也就是在初始化方法执行之后。问PostConstruct、InitializingBean 和 initMethod 的执行顺序是什么答执行顺序是PostConstruct注解标注的方法 →InitializingBean.afterPropertiesSet()方法 → XML 或Bean注解指定的initMethod方法。七、总结Spring 的核心设计思想是控制反转依赖注入是它的实现方式Bean 生命周期是它管理 Bean 的具体过程。这三个概念层层递进共同构成了 Spring IoC 容器的核心骨架。回顾一下全文的核心内容控制反转反转了对象创建和依赖管理的控制权降低了代码耦合度依赖注入有三种方式优先使用构造方法注入Bean 的生命周期分为四个核心阶段每个阶段都有对应的扩展点三个最重要的扩展点BeanPostProcessor、BeanFactoryPostProcessor、InstantiationAwareBeanPostProcessor理解了这些核心概念你就不再是只会用 Spring 的 API 调用者而是真正理解了 Spring 的设计哲学能够从根本上解决开发中遇到的各种问题。