吃透 Spring 全家桶核心原理:从 Bean 生命周期到微服务,面试高频知识点全梳理

发布时间:2026/5/28 17:23:37

吃透 Spring 全家桶核心原理:从 Bean 生命周期到微服务,面试高频知识点全梳理 一、Spring Bean 完整生命周期我们平时自己创建 Java 对象只需要简单new一下使用完毕后交给 JVM 垃圾回收整个过程非常简单。但在 Spring 框架中对象不再由开发者手动创建和管理而是统一交给 Spring IOC 容器托管这个被容器管理的对象我们称之为Bean。Bean 从被 Spring 扫描识别到最终容器关闭被销毁中间会经历十多个步骤这一套完整的流程就是Bean 的生命周期也是 Spring 最基础、面试必考的知识点。1. 扫描类识别待管理组件Spring 项目启动之后第一件事就是进行包扫描。以 SpringBoot 为例SpringBootApplication注解默认会扫描启动类所在包以及所有子包下的类。当框架检测到类上标注了Service、Component、Controller、Repository等注解时就会判定这个类需要交给 IOC 容器管理。这个阶段 Spring 并不会直接创建对象只是做标记识别相当于先把所有 “待管理的类” 全部统计出来。2. 生成 BeanDefinitionBean 说明书识别完所有需要托管的类之后Spring 会为每一个类生成一个BeanDefinition。大家可以把它理解为Bean 的说明书它不会存储对象实例而是专门记录这个 Bean 的所有配置元数据类的全限定名、Bean 名称、作用域单例 / 多例、是否懒加载、依赖的其他 Bean、自定义初始化方法、销毁方法等等。有了这份说明书后续 Spring 创建对象、属性注入、执行回调逻辑都能按照配置有条不紊地执行。3. 实例化创建原始对象有了BeanDefinition之后Spring 开始执行实例化操作本质就是通过反射执行类的构造方法等同于我们手动写new UserService()。这里有一个关键点实例化阶段仅仅是把对象的内存空间开辟出来此时对象内部定义的依赖属性比如被Autowired修饰的成员变量全部都是null还没有完成赋值。简单说现在的对象只是一个 “空壳”。4. 属性注入完成依赖装配实例化完成后进入属性注入阶段。Spring 会解析对象中所有依赖包括Autowired、Resource标注的属性、构造参数等然后从 IOC 容器中找到对应的 Bean 实例通过反射为当前对象的属性赋值。这也是 IOC 控制反转、依赖注入的核心体现开发者不用手动创建依赖对象并赋值全部由容器自动完成。执行完这一步对象的依赖关系就全部补齐了。5. Aware 接口回调如果当前 Bean 实现了一系列Aware接口Spring 会依次执行接口中的方法把容器内部的资源、信息主动传递给 Bean。比如实现ApplicationContextAware接口就能获取到 Spring 容器上下文ApplicationContext实现BeanNameAware可以获取当前 Bean 在容器中的名称。这个阶段的意义就是让 Bean 能够感知到自己所处的容器环境。6. BeanPostProcessor 前置处理BeanPostProcessor是 Spring 提供的全局后置处理器可以理解为所有 Bean 在初始化阶段的统一拦截器。在执行自定义初始化方法之前Spring 会遍历所有实现了BeanPostProcessor接口的类调用其中的postProcessBeforeInitialization前置方法。我们可以在这个统一入口中做通用逻辑比如解析自定义注解、补充对象属性、数据校验等很多中间件、框架的增强逻辑都是基于它实现的。7. 执行自定义初始化方法依赖注入完成后就可以执行开发者自定义的初始化逻辑了。日常开发中常用的初始化方式有三种PostConstruct注解、实现InitializingBean接口、XML 配置init-method。这个阶段依赖已经全部注入完毕所以我们可以放心地在初始化方法中调用其他 Bean、初始化连接池、加载缓存数据等业务逻辑。8. BeanPostProcessor 后置处理初始化方法执行完成后会再次回到BeanPostProcessor调用postProcessAfterInitialization后置方法。Spring AOP 的代理对象就是在这个阶段生成的。如果当前 Bean 需要被切面增强Spring 不会把原始对象放入容器而是在这里生成代理对象并返回。9. 放入单例池对外提供服务经过上面所有流程处理后一个完整、可用的 Bean 就成型了。如果是单例 BeanSpring 默认作用域会被存入一级缓存 singletonObjects中也就是我们常说的单例池。后续业务代码中通过Autowired注入获取的就是这个已经处理完成的 Bean。在容器运行期间这个 Bean 会一直常驻内存处理各类业务请求。10. 销毁阶段当 Spring 容器正常关闭时会触发 Bean 的销毁逻辑。我们可以通过PreDestroy注解、实现DisposableBean接口、XML 配置destroy-method三种方式定义销毁方法。一般会在这里做资源释放工作比如关闭数据库连接、停止线程池、清空临时缓存等。二、Spring 循环依赖问题面试重中之重理解完 Bean 生命周期就很容易弄懂循环依赖这也是面试的高频考点。1. 什么是循环依赖循环依赖就是两个或多个 Bean 互相依赖对方。举个最简单的例子AService 中注入了 BService同时 BService 中又注入了 AService。Service public class AService { Autowired private BService bService; } Service public class BService { Autowired private AService aService; }两个对象彼此需要对方形成了循环引用。很多初学者会疑惑为什么我们自己手动new对象会直接报错而 Spring 却能解决这个问题2. 核心解决方案三级缓存Spring 解决单例 Bean 的属性注入循环依赖核心依靠三级缓存三个缓存各司其职一级缓存 singletonObjects存放完全初始化完毕、可以直接使用的完整 Bean二级缓存 earlySingletonObjects存放已经实例化、但还未完成属性注入和初始化的半成品 Bean三级缓存 singletonFactories存放ObjectFactory对象也就是 Bean 的工厂对象用于延迟生成 Bean尤其是代理对象。3. 完整执行流程Spring 开始创建 AService先实例化得到原始对象半成品此时属性还未赋值为了避免循环依赖Spring 将创建 AService 的ObjectFactory存入三级缓存提前暴露半成品对象开始为 AService 做属性注入发现依赖 BService于是转而创建 BService创建 BService 并完成实例化后属性注入阶段发现依赖 AServiceSpring 先去一级缓存查找 AService没有找到再去二级缓存查找依旧没有最后从三级缓存拿到 AService 对应的ObjectFactory通过工厂获取半成品 AService将这个半成品 AService 存入二级缓存同时删除三级缓存中的工厂对象BService 成功完成属性注入、初始化存入一级缓存回到 AService 的属性注入流程此时 BService 已经就绪AService 顺利完成所有流程最终存入一级缓存最后清空二级缓存中的临时数据循环依赖问题彻底解决。4. 为什么构造器注入的循环依赖无法解决上面的方案只适用于属性注入的循环依赖如果是构造器注入Spring 会直接抛出异常。原因很简单构造器注入要求在实例化阶段就必须拿到依赖对象。而实例化是对象创建的第一步此时还没有半成品对象更无法提前暴露三级缓存机制完全无法生效因此构造器循环依赖 Spring 无法处理。5. 为什么需要三级缓存两级不行有同学会问只用一级和二级缓存难道不够吗答案是不行核心原因是AOP 代理。如果 Bean 需要 AOP 增强最终对外提供的是代理对象而非原始对象。如果直接把原始半成品 Bean 放入二级缓存其他 Bean 注入的就是原始对象和最终容器中的代理对象不一致。三级缓存中存储的是ObjectFactory工厂只有当对象被其他 Bean 依赖时才会调用工厂方法生成对象如果需要 AOP 则直接生成代理对象保证所有地方注入的都是同一个对象这就是三级缓存存在的意义。三、Spring AOP 与事务原理AOP面向切面编程是 Spring 两大核心之一另一核心是 IOC日常开发中的日志、权限校验、事务控制、接口耗时统计全部都依赖 AOP 实现。1. AOP 核心本质动态代理AOP 的设计目标很明确在不修改原有业务代码的前提下对方法进行功能增强。它的底层核心就是 Java 动态代理结合我们前面讲到的 Bean 生命周期代理对象正是在BeanPostProcessor后置处理阶段生成的。当 Spring 检测到当前 Bean 存在切面、Transactional等需要增强的逻辑时不会把原始对象放入容器而是生成代理对象。后续所有方法调用都会先走代理对象再由代理对象去调用原始目标方法。Spring 提供两种动态代理实现方式JDK 动态代理要求目标类必须实现接口基于接口生成代理对象CGLIB 动态代理无需实现接口通过继承目标类生成子类作为代理对象SpringBoot 默认使用该方式。2. AOP 执行流程业务代码调用目标方法实际调用的是代理对象的方法代理对象拦截请求执行前置增强逻辑比如打印日志、开启事务通过反射调用原始目标对象的业务方法业务方法执行完毕后执行后置增强逻辑比如提交事务、统计耗时如果业务方法抛出异常执行异常增强逻辑比如事务回滚、异常告警最终将结果返回给调用方。3. Transactional 事务失效常见场景Spring 声明式事务基于 AOP 实现所以大部分事务失效问题本质都是代理没有生效结合实际开发总结三类高频失效场景场景一类内部方法调用Service public class OrderService { public void test() { // 内部调用没有走代理对象 createOrder(); } Transactional public void createOrder() { // 业务逻辑 } }test方法内部直接调用本类的createOrder属于原生对象内部调用不会经过代理对象切面逻辑无法执行事务自然失效。场景二方法非 public 修饰Spring AOP 默认只会对public方法生成代理如果事务方法定义为private、protected代理无法生效事务失效。场景三异常被捕获 / 异常类型不匹配如果业务代码手动try-catch捕获了所有异常代理无法感知异常就不会触发回滚Spring 事务默认只对 RuntimeException 运行时异常回滚如果抛出受检异常Exception也不会触发回滚。4. Spring 事务底层原理Spring 事务并不是凭空创造的而是对原生 JDBC 事务做了封装整体架构AOP 动态代理 ThreadLocal 数据库事务。带有Transactional的 Bean启动时生成代理对象调用方法时进入代理逻辑从数据源获取数据库连接关闭自动提交将数据库连接存入ThreadLocal保证同一个线程内所有数据库操作使用同一个连接执行业务 SQL执行成功则手动提交事务一旦捕获异常立刻执行事务回滚操作释放连接。简单来说Spring 只是把我们手写的 JDBC 事务模板代码通过 AOP 自动织入到方法前后极大简化了开发。四、注解底层原理现在的 Spring 开发几乎离不开注解Service、Autowired、Transactional、RequestMapping…… 注解让我们告别了繁琐的 XML 配置。很多人只会用注解却不清楚它的底层逻辑。1. 注解的本质注解本质就是附加在类、方法、字段上的元数据标签它本身不具备任何执行能力仅仅是一段描述信息。代码编译后注解信息会被保留在 Class 文件中。注解想要生效必须依靠框架扫描 反射解析框架启动时扫描所有类通过反射判断类 / 方法上是否存在指定注解再根据注解执行对应的业务逻辑。2. Retention 注解生命周期注解有一个核心元注解Retention用来定义注解的存活范围也是注解能否在运行时被读取的关键SOURCE仅存在于源码阶段编译后丢失运行时无法读取CLASS保留到 Class 文件中JVM 运行时不会加载反射无法获取RUNTIME运行时依旧保留可以通过反射读取Spring 所有核心注解都是该类型。3. Autowired 底层原理Autowired自动注入的流程非常清晰Spring 在属性注入阶段扫描类中所有被Autowired修饰的字段根据字段类型 / 名称去 IOC 容器中匹配对应的 Bean 实例利用 Java 反射field.set()方法手动为字段赋值完成自动注入。说白了Autowired就是框架帮我们省略了手动获取 Bean、手动赋值的过程。五、SpringMVC 核心流程与原理SpringMVC 是 Spring 体系下的 Web 框架专门用来处理 HTTP 请求现在所有 SpringBoot Web 项目底层都依赖它。1. 核心入口DispatcherServletDispatcherServlet是 SpringMVC 的前端控制器也是整个请求流程的总调度中心。所有浏览器发起的 HTTP 请求都会先经过 Tomcat 容器再统一进入DispatcherServlet由它进行分发处理。在传统原生 Servlet 开发中一个接口就要定义一个 Servlet管理起来极其繁琐。而 SpringMVC 采用前端控制器模式用一个统一入口接收所有请求彻底解决了原生 Servlet 的弊端。2. SpringMVC 完整请求流程浏览器发起 HTTP 请求Tomcat 监听端口建立连接并解析 HTTP 协议请求进入DispatcherServlet核心方法doDispatch开始调度HandlerMapping根据请求 URL匹配到对应的 Controller 和目标方法HandlerAdapter接管目标方法开始做参数绑定、类型转换执行 Controller 中的业务方法得到返回结果如果返回视图名称由ViewResolver视图解析器渲染页面如果返回 JSON 数据由HttpMessageConverter做序列化转换组装响应数据返回给浏览器。3. 参数绑定原理我们在 Controller 方法中直接定义参数Spring 就能自动把请求数据赋值进去这就是参数绑定。底层逻辑HandlerAdapter会调用对应的参数解析器不同注解对应不同解析器RequestParam、PathVariable、RequestBody等。解析器从HttpServletRequest中提取请求数据再通过类型转换器把字符串转为 Java 对应类型最后通过反射调用目标方法并传参。4. 拦截器与过滤器区别在 Web 开发中Filter、Interceptor、AOP 都可以做拦截增强三者层级完全不同也是面试常考点Filter过滤器属于 Servlet 规范工作在 Tomcat 和 DispatcherServlet 之间偏向底层请求过滤Interceptor拦截器属于 SpringMVC 组件工作在 DispatcherServlet 内部可以获取 Controller 方法信息适合做登录校验、JWT 鉴权AOP属于 Spring Bean 层工作在 Service 方法执行前后适合做事务、日志、方法增强。执行顺序请求 → Filter → Interceptor → Controller → AOP → Service六、SpringBoot 自动装配原理SpringBoot 的出现最大的价值就是简化配置告别 SSM 时代繁琐的 XML 文件、手动配置组件。而它实现简化的核心就是自动装配。1. 核心入口SpringBootApplication启动类上的SpringBootApplication是一个组合注解其中最核心的就是EnableAutoConfiguration作用就是开启自动装配功能。2. 自动装配完整流程项目启动EnableAutoConfiguration生效框架加载AutoConfigurationImportSelector读取指定配置文件SpringBoot2 读取META-INF/spring.factoriesSpringBoot3 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports配置文件中定义了大量自动配置类XXXAutoConfiguration比如 WebMvc 自动配置、Jackson 序列化配置、Tomcat 配置等加载这些配置类配置类上标注Configuration内部通过Bean向 IOC 容器注册组件配合条件注解ConditionalOnClass、ConditionalOnMissingBean做判断只有 classpath 下存在对应类、容器中没有手动创建 Bean 时自动配置才会生效。3. Starter 依赖的作用我们引入spring-boot-starter-web这类依赖本质并不是引入功能代码而是通过 Maven 传递依赖把 spring-webmvc、tomcat、jackson 等核心 Jar 包引入项目。Jar 包加入后classpath 下就会出现对应类触发条件注解对应的自动配置类生效相关组件自动注册到容器我们无需任何配置就能直接使用 Web 功能。七、Spring、SpringMVC、SpringBoot、SpringCloud 关系很多初学者分不清这四个框架的定位其实它们是层层递进、依赖兼容的关系Spring基础核心框架提供 IOC、AOP、事务等底层能力是所有框架的基石SpringMVCSpring 的 Web 模块专门处理 HTTP 请求构建 Web 应用SpringBoot基于 Spring 和 SpringMVC主打自动装配简化项目搭建和配置快速开发单体应用SpringCloud基于 SpringBoot一套完整的微服务解决方案提供服务注册发现、网关、配置中心、熔断限流、远程调用等分布式能力。简单总结Spring 是地基SpringMVC 做 WebSpringBoot 简化开发SpringCloud 搭建微服务。八、微服务架构与 Nacos 核心原理1. 单体架构的痛点传统单体架构所有业务模块用户、订单、商品、支付打包成一个 Jar 包部署在一台服务器。优点是开发、部署简单但随着业务扩张问题越来越明显代码臃肿模块耦合严重维护成本高无法按模块独立扩容一个模块出现问题整体服务崩溃技术栈统一无法针对不同模块选择适配的技术。2. 微服务架构介绍微服务就是按照业务领域拆分系统把用户、订单、商品拆分成独立的工程每个服务独立开发、独立部署、独立扩容、独立使用数据库。拆分之后原本 JVM 内部的本地方法调用变成了跨进程、跨机器的网络调用因此需要配套的分布式组件支撑。3. OpenFeign 远程调用原理微服务之间不能直接本地调用于是出现了 RPC 框架SpringCloud 中的 OpenFeign 就是典型代表。它底层基于 HTTP 请求结合动态代理把网络请求封装成接口方法调用。开发者只需要定义 Feign 接口标注服务名和请求路径调用接口方法就等同于发起远程 HTTP 请求极大降低了远程调用的开发成本。4. Nacos 核心功能与原理Nacos 是微服务架构中常用的组件两大核心功能服务注册与发现、配置中心。1服务注册与发现微服务启动后自动将自身服务名、IP、端口注册到 Nacos 服务端服务启动后定时发送心跳包默认 5 秒一次证明服务健康在线服务消费者从 Nacos 拉取服务实例列表通过负载均衡选择实例发起调用长时间未收到心跳Nacos 判定服务下线自动剔除实例。2临时实例 永久实例临时实例默认类型依赖心跳服务宕机后自动从 Nacos 剔除适用于常规业务服务永久实例不依赖心跳服务宕机也不会自动删除需要手动下线适用于数据库、MQ 等中间件服务。3CAP 理论与 Nacos 选型分布式领域 CAP 理论一致性 (C)、可用性 (A)、分区容错 (P)。分布式环境下网络分区 (P) 无法避免因此只能在 C 和 A 之间二选一AP 架构优先保证可用性数据短暂不一致也正常返回Nacos服务注册采用 APCP 架构优先保证数据强一致性牺牲部分可用性Nacos配置中心采用 CP。九、总结整篇内容从 Spring 最基础的 Bean 生命周期出发延伸到循环依赖、AOP、事务、注解、SpringMVC、SpringBoot 自动装配最后拓展到微服务架构、OpenFeign、Nacos 等分布式组件覆盖了 Java 后端面试和日常开发中 80% 以上的高频知识点。这些技术并不是孤立存在的而是环环相扣Bean 生命周期是基础AOP 基于 Bean 的后置处理器实现事务基于 AOPSpringBoot 自动装配基于 Spring 的注解和 Bean 管理微服务则是在 SpringBoot 之上做的分布式扩展。日常开发中我们不能只停留在 “会用注解写代码” 的阶段理解底层执行流程和设计思想不仅能快速排查线上问题在面试中也能脱颖而出。后续大家可以结合源码、断点调试一步步跟踪执行流程把理论知识落到实处真正吃透这套 Java 主流技术栈。

相关新闻