Spring框架
面试权重:★★★ | 适用级别:P7/P8+ | 预计复习时间:1周
概览
面试权重:★★★ | 适用级别:P7/P8+ | 预计复习时间:1周
这章最容易被问成源码链路题。真正拿分点不是背几个注解,而是能把 IOC、AOP、事务、自动装配和 MVC 请求流程连成一条清晰的容器工作路径。
建议用法
复习时优先按“容器启动 -> Bean 创建 -> 代理增强 -> 请求处理 -> 事务生效”这条链路串讲。高频题只用来抽背,不代替正文理解。
一、知识体系
1. Spring Core ★★★
1.1 IOC 容器与依赖注入 ★★★
IOC 与 DI 到底解决了什么
- 核心结论
- IOC 的本质是把对象创建、依赖装配和生命周期管理从业务代码手里拿出来,交给容器统一处理。
- DI 是 IOC 的主要实现方式,容器负责把依赖注入到对象中,而不是业务代码到处
new。
- 原理展开
- 如果对象创建分散在业务代码里,依赖关系会快速固化,测试替身、环境切换、能力扩展都很困难。
- Spring 把 Bean 定义注册到容器后,容器统一负责实例化、依赖解析、初始化回调、代理增强和销毁。
- 所以 IOC 的价值不只是“少写 new”,而是让对象图可管理、可替换、可扩展。
- 面试怎么答
- “IOC 解决的是对象管理权归属问题,DI 解决的是依赖如何注入问题。Spring 容器统一接管对象生命周期后,AOP、事务、事件、条件装配这些能力才有基础。”
构造器注入、Setter 注入、字段注入
- 核心结论
- 生产代码优先构造器注入,Setter 注入适合可选依赖,字段注入最省事但不利于测试和约束表达。
- 原理展开
- 构造器注入能明确表达必填依赖,并天然支持
final字段和不可变设计。 - Setter 注入适合配置型或可选能力,字段注入虽然简洁,但会让依赖关系隐形化,也更难做单测和静态检查。
- 构造器注入能明确表达必填依赖,并天然支持
- 面试怎么答
- “我更倾向构造器注入,因为它能把依赖关系显式化,也能避免 Bean 初始化后依赖缺失。字段注入在面试里可以提,但最好同时指出它的工程问题。”
BeanFactory vs ApplicationContext
- 核心结论
- BeanFactory 是最基础的容器抽象,ApplicationContext 是面向企业级应用的增强容器。
- 原理展开
- BeanFactory 提供最基本的 Bean 注册和获取能力,默认偏延迟加载。
- ApplicationContext 在此基础上增加了事件发布、国际化、资源加载、环境抽象、自动注册后处理器、与 AOP/MVC 等模块集成。
- 大多数 Spring 应用真正用的都是 ApplicationContext,而不是直接操作 BeanFactory。
- 面试怎么答
- “BeanFactory 更像底层容器接口,ApplicationContext 才是业务开发真实接触的容器。面试里除了加载时机,更要讲它为什么能承载 Spring 整个生态能力。”
注解扫描机制
- 核心结论
- 注解扫描的目标是把候选组件变成 BeanDefinition,再交给容器统一管理。
- 原理展开
@ComponentScan会基于包路径扫描类元信息,把命中条件的类注册为 Bean 定义。@Component、@Service、@Repository、@Controller本质上都属于组件标记,后面三个更多是语义化分层。
- 面试怎么答
- “注解扫描不是直接创建对象,而是先注册 BeanDefinition。真正实例化发生在容器刷新阶段,这也是后续条件装配、后处理器和代理增强能介入的关键。”
1.2 Bean 生命周期 ★★★
完整生命周期链路
- 核心结论
- Bean 生命周期不是一串孤立回调,而是一条从定义、实例化、依赖注入、初始化、代理增强到销毁的完整流水线。
- 原理展开
- 实例化:通过构造器或工厂方法创建对象。
- 属性填充:处理
@Autowired、@Value等依赖注入。 - Aware 回调:注入容器基础设施能力。
- 初始化前:执行
BeanPostProcessor#postProcessBeforeInitialization。 - 初始化:执行
@PostConstruct、afterPropertiesSet()、init-method。 - 初始化后:执行
postProcessAfterInitialization,AOP 代理通常在这里生成。 - 使用阶段:Bean 进入正常服务状态。
- 销毁阶段:执行
@PreDestroy、destroy()、destroy-method。
- 面试怎么答
- “生命周期里最关键的不是背顺序,而是讲清 Spring 在哪些节点插入扩展点,尤其是依赖注入、BeanPostProcessor 和 AOP 代理生成。”
BeanPostProcessor 的作用
- 核心结论
- BeanPostProcessor 是 Spring 容器扩展能力的核心入口之一。
- 原理展开
- 很多看起来像“注解魔法”的能力,本质上都是后处理器在特定阶段对 Bean 做了增强。
@Autowired、@PostConstruct、AOP 代理、部分校验与元数据处理,都离不开相关的 BeanPostProcessor。
- 面试怎么答
- “如果只让我挑一个 Spring Core 的关键扩展点,我会说 BeanPostProcessor。很多框架能力本质都是在 Bean 初始化前后接管或增强对象。”
面试高频:为什么 AOP 代理通常在初始化后生成
- 核心结论
- 因为 Spring 需要先拿到目标 Bean 的基础形态,再决定是否包一层代理返回给容器和调用方。
- 原理展开
- 如果代理生成太早,依赖注入和初始化链路会变复杂;如果太晚,外部拿到的就不是统一代理对象。
- 所以常见做法是在
postProcessAfterInitialization阶段统一判断并返回代理。
- 面试怎么答
- “AOP 不会在类扫描时就直接生成代理,而是在 Bean 已经完成基础初始化后决定是否包装,这样既能保证依赖注入完整,也能让外部尽量只感知代理对象。”
1.3 AOP 原理 ★★★
JDK 动态代理 vs CGLIB
- 核心结论
- JDK 动态代理基于接口,CGLIB 基于继承生成子类;二者的选择取决于目标类是否有接口以及框架配置。
- 原理展开
- JDK 动态代理通过
Proxy生成代理类,代理调用会路由到InvocationHandler。 - CGLIB 通过生成子类重写方法实现拦截,因此无法代理
final类或final方法。 - Spring Boot 新项目里常见默认走类代理,但底层原理仍要区分清楚。
- JDK 动态代理通过
- 面试怎么答
- “JDK 代理和 CGLIB 的核心差别不是性能,而是代理模型不同。JDK 基于接口,CGLIB 基于继承。真正答题时还要顺带点出 final 限制和 Spring 的选择策略。”
切面核心概念与执行链
- 核心结论
- 切点决定拦谁,通知决定怎么增强,代理对象负责把增强逻辑织入到目标方法调用链。
- 原理展开
@Before、@After、@Around、@AfterReturning、@AfterThrowing是常见通知类型。@Around最强,因为它能完全包裹目标方法并控制是否继续执行。- 多个切面叠加时,本质是多层拦截器链,顺序受
@Order或优先级影响。
- 面试怎么答
- “AOP 本质不是修改原类字节码,而是返回一个代理对象,把目标方法调用包装成一条增强链。日志、事务、权限这类横切逻辑都适合放在这层。”
AOP 的适用边界
- 核心结论
- AOP 适合横切关注点,不适合承载复杂业务流程编排。
- 易错点
- 切面里做远程调用、数据库操作或重业务逻辑,容易让链路变隐形,排障成本很高。
- 面试怎么答
- “AOP 适合做统一日志、鉴权、埋点、事务这些横切能力,不适合把核心业务流程藏到切面里,否则代码可读性和可维护性都会明显变差。”
1.4 循环依赖 ★★★
三级缓存解决机制
- 核心结论
- 三级缓存的目标不是为了“多一层更复杂”,而是为了在处理单例循环依赖时,同时兼顾早期引用暴露和代理对象一致性。
- 原理展开
- 一级缓存
singletonObjects:存完全初始化好的单例。 - 二级缓存
earlySingletonObjects:存提前暴露的早期对象引用。 - 三级缓存
singletonFactories:存生成早期引用的工厂,必要时可返回代理对象。 - 创建 A 时先实例化并把工厂放入三级缓存;创建 B 依赖 A 时,可从三级缓存拿到 A 的早期引用,等双方初始化完成再进入一级缓存。
- 一级缓存
- 面试怎么答
- “三级缓存解决的关键不是循环本身,而是‘提前暴露什么对象’。如果有 AOP,必须通过 ObjectFactory 延迟决定返回原始对象还是代理对象,这就是三级缓存存在的意义。”
为什么不能只用二级缓存
- 核心结论
- 因为只用二级缓存无法优雅处理代理对象生成时机,容易出现同一个 Bean 暴露过原始对象和代理对象两份引用。
- 面试怎么答
- “二级缓存能解决部分早期引用问题,但遇到 AOP 时,Spring 还需要一个能延迟创建引用的工厂层,这样才能保证外部最终拿到的是一致对象。”
循环依赖的边界与治理
- 核心结论
- Spring 只能解决单例 Bean、字段或 Setter 注入场景下的一部分循环依赖,构造器循环依赖解决不了。
- 原理展开
- 从工程角度看,循环依赖往往意味着职责缠绕,不应该把“Spring 能兜底”当成合理设计。
- 常见治理方式是拆职责、引入中间协调层、改事件驱动或使用
@Lazy过渡。
- 面试怎么答
- “面试里除了说三级缓存,我还会补一句:能解决不代表应该保留,循环依赖通常是结构问题,最好尽快治理。”
2. Spring Boot ★★★
2.1 自动装配原理 ★★★
自动装配主链路
- 核心结论
- 自动装配的本质是:先发现候选配置类,再基于条件注解判断是否生效,最后把对应 Bean 定义注册进容器。
- 原理展开
@SpringBootApplication包含@EnableAutoConfiguration。@EnableAutoConfiguration通过AutoConfigurationImportSelector导入自动配置类。- Spring Boot 2.x 和 3.x 的自动配置文件位置不同,但核心思想一致,都是“声明候选 + 条件过滤 + 容器注册”。
- 面试怎么答
- “自动装配不是启动时胡乱扫一遍依赖,而是先加载候选自动配置类,再通过
@ConditionalOnXxx做精确筛选,最后把满足条件的 Bean 注入容器。”
- “自动装配不是启动时胡乱扫一遍依赖,而是先加载候选自动配置类,再通过
条件注解的作用
- 核心结论
- 条件注解决定了自动配置何时生效,也决定了 Spring Boot 为什么能做到‘约定优于配置但又允许覆盖’。
- 原理展开
@ConditionalOnClass判断类路径依赖是否存在。@ConditionalOnMissingBean确保用户自定义 Bean 可以覆盖默认实现。@ConditionalOnProperty让功能能按配置显式开关。
- 面试怎么答
- “条件注解是自动装配的闸门,没有它们就会变成一锅乱炖。面试里最好明确说出类路径条件、Bean 条件和属性条件分别控制什么。”
Spring Boot 2.x 与 3.x 的差异
- 核心结论
- 3.x 的变化重点是自动配置注册文件、Jakarta 迁移和 JDK 基线提升,而不是自动装配思想发生根本变化。
- 面试怎么答
- “我会先说共性,再补差异:2.x 和 3.x 都是候选配置 + 条件装配,只是 3.x 在自动配置声明方式和技术基线上做了升级。”
2.2 Starter 机制 ★★★
- 核心结论
- Starter 的价值不是简单聚合依赖,而是把一组依赖、默认配置、扩展点和开箱即用体验封装成标准化能力。
- 原理展开
- 一个成熟 Starter 通常包含自动配置模块和依赖聚合模块。
- 自动配置里要提供合理默认值、可覆盖能力和可观测性,而不是只要能跑就行。
- 面试怎么答
- “自定义 Starter 真正要讲的是设计思路:默认配置怎么给、用户怎么覆盖、配置项怎么暴露、版本兼容怎么控制,而不只是注册一个配置类。”
2.3 @Transactional 深度 ★★★
传播行为与隔离级别
- 核心结论
- 传播行为解决的是“当前方法进入时如何处理已有事务”,隔离级别解决的是“并发事务之间彼此看见什么”。
- 原理展开
REQUIRED最常见,加入当前事务或新建事务。REQUIRES_NEW会挂起外层事务,适合独立提交的日志、审计、补偿记录。NESTED依赖保存点,更适合局部失败可回滚但外层事务继续的场景。
- 面试怎么答
- “传播行为和隔离级别是两套不同维度,答题时别混。前者看调用栈里的事务关系,后者看并发读写可见性。”
事务为什么会失效
- 核心结论
- 事务失效大多数不是数据库问题,而是 Spring 代理没接管到那次方法调用。
- 原理展开
- 非
public方法、自调用、对象不是 Spring Bean、异常被吞掉、抛出 checked exception 未配置回滚,这些都属于高频失效场景。 - 本质上
@Transactional依赖代理拦截,如果没有经过代理,自然没有事务边界。
- 非
- 面试怎么答
- “事务失效先不要怀疑数据库,先看调用有没有经过 Spring 代理。大多数线上问题都能归到这条主线。”
排查与修复思路
- 核心结论
- 事务问题排查要同时看代理、生效边界、异常传播和事务管理器配置。
- 面试怎么答
- “我的排查顺序一般是:这个类是不是 Spring 管理的、调用有没有走代理、方法权限和异常传播是否符合规则、底层事务管理器和数据源是否匹配。”
- 易错点
- 不要机械地把所有异常都配
rollbackFor = Exception.class,要结合业务语义判断。
- 不要机械地把所有异常都配
3. Spring MVC ★★
3.1 DispatcherServlet 请求处理流程 ★★★
- 核心结论
- DispatcherServlet 是前端控制器,负责把一次 HTTP 请求分发到正确的 Handler,再把执行结果转换成最终响应。
- 原理展开
- 请求先进入 DispatcherServlet。
- 通过 HandlerMapping 找到目标 Handler。
- 通过 HandlerAdapter 做参数解析、类型转换并执行 Controller 方法。
- 返回值再交给视图解析器或消息转换器处理。
- 最终写回响应。
- 前后端分离场景下,真正高频的是
HttpMessageConverter如何把对象和 JSON 做双向转换。
- 面试怎么答
- “我通常把 MVC 流程概括成路由定位、方法执行、结果渲染三步,再补 HandlerMapping、HandlerAdapter、MessageConverter 这三个关键扩展点。”
3.2 拦截器 vs 过滤器 ★★
- 核心结论
- Filter 属于 Servlet 规范层,Interceptor 属于 Spring MVC 层;二者所处位置不同,拿到的上下文能力也不同。
- 原理展开
- Filter 更适合字符编码、CORS、通用审计、底层安全过滤。
- Interceptor 更适合登录态校验、方法级日志、请求上下文注入等与 Handler 紧密相关的逻辑。
- 面试怎么答
- “我会用‘所处层次不同’来答:Filter 在 Spring MVC 之前,Interceptor 在 Handler 周围,所以能拿到的语义信息和适用场景不同。”
3.3 参数解析与数据绑定 ★
- 核心结论
- 参数解析的本质是把 HTTP 协议层数据映射成方法入参,再做类型转换、校验和错误处理。
- 原理展开
@RequestParam、@PathVariable、@RequestBody只是入口形式不同,底层都要经过参数解析器和消息转换器。- 复杂场景可通过
HandlerMethodArgumentResolver自定义统一解析逻辑。
- 面试怎么答
- “面试里这块不用讲太散,围绕参数来源、类型转换、校验和自定义解析扩展点即可。”
二、高频面试题
基础级(P7必答)
- IOC 容器有哪些类型?ApplicationContext 比 BeanFactory 多了什么?
- 30秒答法
- BeanFactory 是最基础的 IOC 容器,只提供 Bean 注册和获取等基础能力;ApplicationContext 在其上扩展了事件、国际化、资源加载、环境抽象、AOP 集成和大量企业级功能。业务开发真正常用的是 ApplicationContext,而不是直接操作 BeanFactory。
- 关键词
- BeanFactory
- ApplicationContext
- 企业级扩展
- 预加载
- 追问提醒
- 继续准备“为什么 Spring 生态能力大多依赖 ApplicationContext”。
- Bean 的完整生命周期是怎样的?BeanPostProcessor 在哪个环节起作用?
- 30秒答法
- 生命周期主线是实例化、属性填充、Aware 回调、初始化前后处理、正式使用、销毁。BeanPostProcessor 在初始化前后介入,是 Spring 扩展能力的核心入口,像
@Autowired处理、AOP 代理生成都离不开它。
- 生命周期主线是实例化、属性填充、Aware 回调、初始化前后处理、正式使用、销毁。BeanPostProcessor 在初始化前后介入,是 Spring 扩展能力的核心入口,像
- 关键词
- 实例化
- 属性填充
- BeanPostProcessor
- AOP 代理
- 追问提醒
- 继续准备“为什么代理通常在初始化后生成”。
- AOP 动态代理如何选择?JDK 代理与 CGLIB 的区别?
- 30秒答法
- JDK 动态代理基于接口,CGLIB 基于继承生成子类;有接口时可以用 JDK,没有接口时通常走 CGLIB。真正要答到位的是它们的限制和适用边界,比如 CGLIB 不能代理 final 方法,JDK 代理要求接口抽象。
- 关键词
- Proxy
- CGLIB
- 接口代理
- final 限制
- 追问提醒
- 继续准备“Spring Boot 默认代理策略”和“多层代理如何理解”。
- Spring 如何解决循环依赖?为什么需要三级缓存?
- 30秒答法
- Spring 通过三级缓存解决部分单例循环依赖:一级缓存放完整 Bean,二级缓存放早期引用,三级缓存放 ObjectFactory。之所以需要三级缓存,是为了在存在 AOP 时延迟决定返回原始对象还是代理对象,保证同一个 Bean 对外暴露一致引用。
- 关键词
- singletonObjects
- earlySingletonObjects
- singletonFactories
- AOP 代理一致性
- 追问提醒
- 继续准备“为什么构造器循环依赖解决不了”。
- @Transactional 有哪些常见失效场景?如何排查和修复?
- 30秒答法
- 常见失效点包括自调用、非 public 方法、对象不是 Spring Bean、异常被吞、checked exception 未配置回滚。排查要先确认是否经过代理,再看异常传播和事务管理器是否匹配,修复优先通过拆分类和调整调用边界,而不是堆技巧。
- 关键词
- 自调用
- 代理失效
- rollbackFor
- 事务管理器
- 追问提醒
- 继续准备“为什么 this 调用不会生效”和“如何优雅修复自调用问题”。
- Spring Boot 自动装配的原理是什么?@Conditional 系列注解有什么作用?
- 30秒答法
- 自动装配会先加载候选自动配置类,再根据
@ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty等条件注解决定是否生效,最后把满足条件的 Bean 注册进容器。这套机制让 Spring Boot 既能开箱即用,又允许用户自定义覆盖。
- 自动装配会先加载候选自动配置类,再根据
- 关键词
- AutoConfigurationImportSelector
- 条件装配
- 候选配置类
- 约定优于配置
- 追问提醒
- 继续准备“2.x 和 3.x 自动配置声明方式有什么差异”。
- Spring MVC 中 DispatcherServlet 的请求处理流程是怎样的?
- 30秒答法
- 请求先到 DispatcherServlet,再由 HandlerMapping 定位 Handler,HandlerAdapter 完成参数解析和方法执行,返回结果再交给 MessageConverter 或 ViewResolver 处理。真正高频的是理解前端控制器模式和几个关键扩展点,而不是死背每一步名字。
- 关键词
- DispatcherServlet
- HandlerMapping
- HandlerAdapter
- MessageConverter
- 追问提醒
- 继续准备“
@ResponseBody为什么不走视图解析”。
- 继续准备“
- Spring 事务的传播行为有哪些?各自适用什么场景?
- 30秒答法
- 常见传播行为里,
REQUIRED适合大多数业务链路,REQUIRES_NEW适合独立提交的日志或补偿,NESTED适合局部失败可回滚的批处理场景。答题时最好顺带说明传播行为处理的是事务嵌套关系,不是并发隔离问题。
- 常见传播行为里,
- 关键词
- REQUIRED
- REQUIRES_NEW
- NESTED
- savepoint
- 追问提醒
- 继续准备“
REQUIRES_NEW为什么会带来额外资源开销”。
- 继续准备“
- Spring Boot 启动流程是怎样的?自动装配在哪个阶段发生?
- 30秒答法
SpringApplication.run()会创建并准备 ApplicationContext,加载环境和初始化器,解析配置类并注册 BeanDefinition,在容器刷新阶段完成 Bean 创建和自动装配。自动装配本质发生在配置类解析和 BeanDefinition 注册阶段,后面才是实例化与初始化。
- 关键词
- SpringApplication.run
- BeanDefinition
- refresh
- 配置类解析
- 追问提醒
- 继续准备“Runner 在什么时机执行”。
- @ConditionalOnBean 的作用?如何自定义条件注解?
- 30秒答法
@ConditionalOnBean表示容器里存在某类或某个 Bean 时当前配置才生效,常用于把能力建立在已有基础设施上。自定义条件注解的核心是实现Condition接口,根据环境、BeanFactory、类路径等上下文动态判断是否装配。
- 关键词
- Conditional
- Condition
- Bean 条件
- 动态装配
- 追问提醒
- 继续准备“和
@ConditionalOnMissingBean的配合方式”。
- 继续准备“和
- Spring 的注解扫描机制?@ComponentScan 如何过滤?
- 30秒答法
@ComponentScan会扫描指定包路径下的候选组件,把命中的类注册成 BeanDefinition。过滤通常通过 includeFilters 和 excludeFilters 控制,常见维度有注解、类型、正则和自定义规则。
- 关键词
- ComponentScan
- BeanDefinition
- includeFilters
- excludeFilters
- 追问提醒
- 继续准备“为什么启动类包路径会影响默认扫描范围”。
- Spring MVC 中拦截器和过滤器的区别?
- 30秒答法
- Filter 属于 Servlet 层,对所有请求生效,位置在 DispatcherServlet 前后;Interceptor 属于 Spring MVC 层,只围绕 Handler 执行。前者适合通用协议层处理,后者适合业务相关的请求增强和校验。
- 关键词
- Servlet 规范
- MVC 层
- preHandle
- 协议层处理
- 追问提醒
- 继续准备“异常处理链路里二者分别如何参与”。
进阶级(P8+深挖)
- 事务传播行为有哪些?NESTED 是如何实现的?
- 30秒答法
NESTED本质上是当前事务内部创建保存点,子流程失败可以回滚到保存点,而不必直接回滚整个外层事务。它依赖数据库 savepoint 能力,所以答题时要强调它和REQUIRES_NEW完全不是一回事,一个是同一物理事务内分段回滚,一个是新开独立事务。
- 关键词
- savepoint
- NESTED
- 物理事务
- 部分回滚
- 追问提醒
- 继续准备“不同数据库和事务管理器下的支持差异”。
- 如何自定义一个 Spring Boot Starter?设计要点是什么?
- 30秒答法
- 自定义 Starter 通常拆成自动配置模块和依赖聚合模块。设计要点不是能不能装配成功,而是默认值是否合理、用户是否容易覆盖、配置项是否可观测、版本兼容是否可控。
- 关键词
- autoconfigure
- starter
- 默认配置
- 用户覆盖
- 追问提醒
- 继续准备“
@ConfigurationProperties如何设计更稳”。
- 继续准备“
- @Transactional 的自调用问题除了 AopContext,还有哪些解决方案?各有什么优劣?
- 30秒答法
- 常见方案有拆到独立 Bean、注入自身代理、
AopContext.currentProxy()、AspectJ 编织。工程上最推荐拆类,因为它最符合职责分离;其余方案更多是兼容性补救,虽然能用,但都会引入额外复杂度或侵入性。
- 常见方案有拆到独立 Bean、注入自身代理、
- 关键词
- 自调用
- 代理边界
- self injection
- AspectJ
- 追问提醒
- 继续准备“为什么拆类通常是最优解”。
- Spring AOP 在高并发场景下有什么性能影响?如何优化?
- 30秒答法
- AOP 本身的代理开销通常不是瓶颈,真正的性能问题大多来自切面里做了重逻辑、远程调用或过宽切点导致增强范围失控。优化方向是缩小切点、减少切面内重操作、明确多切面顺序并做好观测。
- 关键词
- 切点范围
- 代理开销
- 重逻辑
- 观测
- 追问提醒
- 继续准备“如何判断瓶颈在切面本身还是业务逻辑”。
- Spring Boot 3.x 相比 2.x 在自动装配机制上有哪些变化?
- 30秒答法
- 3.x 的主要变化包括自动配置注册文件调整、Jakarta 命名空间迁移和 Java 17+ 基线。答题时先讲自动装配核心思想没变,再补充这些升级点,更显得你知道框架演进而不是只背版本差异。
- 关键词
- AutoConfiguration.imports
- Jakarta
- Java 17
- 演进
- 追问提醒
- 继续准备“升级 2.x 到 3.x 时常见兼容坑有哪些”。
三、实战场景题(P8+重点)
- 事务设计:一个下单流程涉及扣库存、创建订单、扣积分、发通知,如何设计事务边界和传播行为?
- 回答框架
- 先区分单服务内强一致动作和跨服务异步动作。
- 主交易链路内部用本地事务保证订单与库存边界内的一致性。
- 积分、通知等更适合走事件驱动和最终一致性,而不是全链路大事务。
- 核心关注点
- 哪些动作必须强一致,哪些允许异步补偿。
REQUIRES_NEW是否被滥用。- 幂等、补偿和重试策略是否完善。
- AOP 实战:如何用 AOP 实现一个通用的接口幂等性校验框架?
- 回答框架
- 通过注解标识幂等入口。
- 切面统一提取幂等键、做分布式锁或去重记录校验。
- 失败时定义统一返回语义和过期策略。
- 核心关注点
- 幂等键如何设计,是否和业务主键一致。
- 锁粒度、超时和异常回滚如何处理。
- 切面只做横切校验,不要吞掉业务语义。
- 自动装配扩展:公司内部有多个微服务共用的配置逻辑,如何设计一个公共 Starter?
- 回答框架
- 抽公共能力边界,拆自动配置和依赖聚合模块。
- 用条件注解控制启用时机,用配置属性暴露可调项。
- 提供默认埋点、日志、链路和容错能力,同时允许业务按需覆盖。
- 核心关注点
- 不能把业务差异硬塞进 Starter。
- 默认值是否安全,是否容易观测和排障。
- 版本兼容和升级策略是否清晰。
- 性能排查:线上应用启动时间从 30 秒增长到 120 秒,如何排查是哪些自动配置或 Bean 初始化导致?
- 回答框架
- 先看启动日志、条件评估报告和关键 Bean 初始化耗时。
- 再定位是自动配置装太多,还是个别 Bean 初始化做了重操作。
- 最后通过懒加载、拆初始化动作、延后连接建立等方式优化。
- 核心关注点
- 是否抓住启动慢的主因,而不是盲目关功能。
- 外部依赖连接、扫描范围、配置中心/注册中心初始化是否拖慢启动。
- 优化后是否影响可用性与预热流程。
- 循环依赖治理:接手的老项目有大量循环依赖,如何制定分阶段治理方案?
- 回答框架
- 先识别高风险链路和核心 Bean,区分“暂时可运行”和“必须立刻治理”。
- 短期用
@Lazy或调整注入方式止血,中期通过职责拆分、事件驱动、门面层重构逐步消除。 - 最后结合架构规范和扫描工具防止新循环继续引入。
- 核心关注点
- 不能把框架兜底当长期方案。
- 治理顺序要先核心链路、后边缘模块。
- 是否有自动化手段持续发现新增循环依赖。
四、学习资源推荐
书籍
- 《Spring 实战》:适合建立 Spring Core、AOP、MVC 基础认知。
- 《Spring Boot 编程思想》:适合理解自动装配、启动链路和扩展机制。
- 《Spring 源码深度解析》:适合深入 IOC、AOP、事务等源码主线。
博客/文章
- Spring 官方文档:最权威,适合查 Core、Boot、MVC、事务边界和版本差异。
- Spring Boot 官方 Reference:适合核对自动配置、配置属性、条件装配和启动机制。
- 美团技术团队相关文章:适合补大型业务场景下的 Spring 实践经验。
视频
- 极客时间《玩转 Spring 全家桶》:适合系统回顾框架全景。
- B站 Spring 源码解析系列:适合按源码链路做面试深化。