面试指南

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 生命周期不是一串孤立回调,而是一条从定义、实例化、依赖注入、初始化、代理增强到销毁的完整流水线。
  • 原理展开
    1. 实例化:通过构造器或工厂方法创建对象。
    2. 属性填充:处理 @Autowired@Value 等依赖注入。
    3. Aware 回调:注入容器基础设施能力。
    4. 初始化前:执行 BeanPostProcessor#postProcessBeforeInitialization
    5. 初始化:执行 @PostConstructafterPropertiesSet()init-method
    6. 初始化后:执行 postProcessAfterInitialization,AOP 代理通常在这里生成。
    7. 使用阶段:Bean 进入正常服务状态。
    8. 销毁阶段:执行 @PreDestroydestroy()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 代理和 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,再把执行结果转换成最终响应。
  • 原理展开
    1. 请求先进入 DispatcherServlet。
    2. 通过 HandlerMapping 找到目标 Handler。
    3. 通过 HandlerAdapter 做参数解析、类型转换并执行 Controller 方法。
    4. 返回值再交给视图解析器或消息转换器处理。
    5. 最终写回响应。
    • 前后端分离场景下,真正高频的是 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必答)

  1. IOC 容器有哪些类型?ApplicationContext 比 BeanFactory 多了什么?
  • 30秒答法
    • BeanFactory 是最基础的 IOC 容器,只提供 Bean 注册和获取等基础能力;ApplicationContext 在其上扩展了事件、国际化、资源加载、环境抽象、AOP 集成和大量企业级功能。业务开发真正常用的是 ApplicationContext,而不是直接操作 BeanFactory。
  • 关键词
    • BeanFactory
    • ApplicationContext
    • 企业级扩展
    • 预加载
  • 追问提醒
    • 继续准备“为什么 Spring 生态能力大多依赖 ApplicationContext”。
  1. Bean 的完整生命周期是怎样的?BeanPostProcessor 在哪个环节起作用?
  • 30秒答法
    • 生命周期主线是实例化、属性填充、Aware 回调、初始化前后处理、正式使用、销毁。BeanPostProcessor 在初始化前后介入,是 Spring 扩展能力的核心入口,像 @Autowired 处理、AOP 代理生成都离不开它。
  • 关键词
    • 实例化
    • 属性填充
    • BeanPostProcessor
    • AOP 代理
  • 追问提醒
    • 继续准备“为什么代理通常在初始化后生成”。
  1. AOP 动态代理如何选择?JDK 代理与 CGLIB 的区别?
  • 30秒答法
    • JDK 动态代理基于接口,CGLIB 基于继承生成子类;有接口时可以用 JDK,没有接口时通常走 CGLIB。真正要答到位的是它们的限制和适用边界,比如 CGLIB 不能代理 final 方法,JDK 代理要求接口抽象。
  • 关键词
    • Proxy
    • CGLIB
    • 接口代理
    • final 限制
  • 追问提醒
    • 继续准备“Spring Boot 默认代理策略”和“多层代理如何理解”。
  1. Spring 如何解决循环依赖?为什么需要三级缓存?
  • 30秒答法
    • Spring 通过三级缓存解决部分单例循环依赖:一级缓存放完整 Bean,二级缓存放早期引用,三级缓存放 ObjectFactory。之所以需要三级缓存,是为了在存在 AOP 时延迟决定返回原始对象还是代理对象,保证同一个 Bean 对外暴露一致引用。
  • 关键词
    • singletonObjects
    • earlySingletonObjects
    • singletonFactories
    • AOP 代理一致性
  • 追问提醒
    • 继续准备“为什么构造器循环依赖解决不了”。
  1. @Transactional 有哪些常见失效场景?如何排查和修复?
  • 30秒答法
    • 常见失效点包括自调用、非 public 方法、对象不是 Spring Bean、异常被吞、checked exception 未配置回滚。排查要先确认是否经过代理,再看异常传播和事务管理器是否匹配,修复优先通过拆分类和调整调用边界,而不是堆技巧。
  • 关键词
    • 自调用
    • 代理失效
    • rollbackFor
    • 事务管理器
  • 追问提醒
    • 继续准备“为什么 this 调用不会生效”和“如何优雅修复自调用问题”。
  1. Spring Boot 自动装配的原理是什么?@Conditional 系列注解有什么作用?
  • 30秒答法
    • 自动装配会先加载候选自动配置类,再根据 @ConditionalOnClass@ConditionalOnMissingBean@ConditionalOnProperty 等条件注解决定是否生效,最后把满足条件的 Bean 注册进容器。这套机制让 Spring Boot 既能开箱即用,又允许用户自定义覆盖。
  • 关键词
    • AutoConfigurationImportSelector
    • 条件装配
    • 候选配置类
    • 约定优于配置
  • 追问提醒
    • 继续准备“2.x 和 3.x 自动配置声明方式有什么差异”。
  1. Spring MVC 中 DispatcherServlet 的请求处理流程是怎样的?
  • 30秒答法
    • 请求先到 DispatcherServlet,再由 HandlerMapping 定位 Handler,HandlerAdapter 完成参数解析和方法执行,返回结果再交给 MessageConverter 或 ViewResolver 处理。真正高频的是理解前端控制器模式和几个关键扩展点,而不是死背每一步名字。
  • 关键词
    • DispatcherServlet
    • HandlerMapping
    • HandlerAdapter
    • MessageConverter
  • 追问提醒
    • 继续准备“@ResponseBody 为什么不走视图解析”。
  1. Spring 事务的传播行为有哪些?各自适用什么场景?
  • 30秒答法
    • 常见传播行为里,REQUIRED 适合大多数业务链路,REQUIRES_NEW 适合独立提交的日志或补偿,NESTED 适合局部失败可回滚的批处理场景。答题时最好顺带说明传播行为处理的是事务嵌套关系,不是并发隔离问题。
  • 关键词
    • REQUIRED
    • REQUIRES_NEW
    • NESTED
    • savepoint
  • 追问提醒
    • 继续准备“REQUIRES_NEW 为什么会带来额外资源开销”。
  1. Spring Boot 启动流程是怎样的?自动装配在哪个阶段发生?
  • 30秒答法
    • SpringApplication.run() 会创建并准备 ApplicationContext,加载环境和初始化器,解析配置类并注册 BeanDefinition,在容器刷新阶段完成 Bean 创建和自动装配。自动装配本质发生在配置类解析和 BeanDefinition 注册阶段,后面才是实例化与初始化。
  • 关键词
    • SpringApplication.run
    • BeanDefinition
    • refresh
    • 配置类解析
  • 追问提醒
    • 继续准备“Runner 在什么时机执行”。
  1. @ConditionalOnBean 的作用?如何自定义条件注解?
  • 30秒答法
    • @ConditionalOnBean 表示容器里存在某类或某个 Bean 时当前配置才生效,常用于把能力建立在已有基础设施上。自定义条件注解的核心是实现 Condition 接口,根据环境、BeanFactory、类路径等上下文动态判断是否装配。
  • 关键词
    • Conditional
    • Condition
    • Bean 条件
    • 动态装配
  • 追问提醒
    • 继续准备“和 @ConditionalOnMissingBean 的配合方式”。
  1. Spring 的注解扫描机制?@ComponentScan 如何过滤?
  • 30秒答法
    • @ComponentScan 会扫描指定包路径下的候选组件,把命中的类注册成 BeanDefinition。过滤通常通过 includeFilters 和 excludeFilters 控制,常见维度有注解、类型、正则和自定义规则。
  • 关键词
    • ComponentScan
    • BeanDefinition
    • includeFilters
    • excludeFilters
  • 追问提醒
    • 继续准备“为什么启动类包路径会影响默认扫描范围”。
  1. Spring MVC 中拦截器和过滤器的区别?
  • 30秒答法
    • Filter 属于 Servlet 层,对所有请求生效,位置在 DispatcherServlet 前后;Interceptor 属于 Spring MVC 层,只围绕 Handler 执行。前者适合通用协议层处理,后者适合业务相关的请求增强和校验。
  • 关键词
    • Servlet 规范
    • MVC 层
    • preHandle
    • 协议层处理
  • 追问提醒
    • 继续准备“异常处理链路里二者分别如何参与”。

进阶级(P8+深挖)

  1. 事务传播行为有哪些?NESTED 是如何实现的?
  • 30秒答法
    • NESTED 本质上是当前事务内部创建保存点,子流程失败可以回滚到保存点,而不必直接回滚整个外层事务。它依赖数据库 savepoint 能力,所以答题时要强调它和 REQUIRES_NEW 完全不是一回事,一个是同一物理事务内分段回滚,一个是新开独立事务。
  • 关键词
    • savepoint
    • NESTED
    • 物理事务
    • 部分回滚
  • 追问提醒
    • 继续准备“不同数据库和事务管理器下的支持差异”。
  1. 如何自定义一个 Spring Boot Starter?设计要点是什么?
  • 30秒答法
    • 自定义 Starter 通常拆成自动配置模块和依赖聚合模块。设计要点不是能不能装配成功,而是默认值是否合理、用户是否容易覆盖、配置项是否可观测、版本兼容是否可控。
  • 关键词
    • autoconfigure
    • starter
    • 默认配置
    • 用户覆盖
  • 追问提醒
    • 继续准备“@ConfigurationProperties 如何设计更稳”。
  1. @Transactional 的自调用问题除了 AopContext,还有哪些解决方案?各有什么优劣?
  • 30秒答法
    • 常见方案有拆到独立 Bean、注入自身代理、AopContext.currentProxy()、AspectJ 编织。工程上最推荐拆类,因为它最符合职责分离;其余方案更多是兼容性补救,虽然能用,但都会引入额外复杂度或侵入性。
  • 关键词
    • 自调用
    • 代理边界
    • self injection
    • AspectJ
  • 追问提醒
    • 继续准备“为什么拆类通常是最优解”。
  1. Spring AOP 在高并发场景下有什么性能影响?如何优化?
  • 30秒答法
    • AOP 本身的代理开销通常不是瓶颈,真正的性能问题大多来自切面里做了重逻辑、远程调用或过宽切点导致增强范围失控。优化方向是缩小切点、减少切面内重操作、明确多切面顺序并做好观测。
  • 关键词
    • 切点范围
    • 代理开销
    • 重逻辑
    • 观测
  • 追问提醒
    • 继续准备“如何判断瓶颈在切面本身还是业务逻辑”。
  1. Spring Boot 3.x 相比 2.x 在自动装配机制上有哪些变化?
  • 30秒答法
    • 3.x 的主要变化包括自动配置注册文件调整、Jakarta 命名空间迁移和 Java 17+ 基线。答题时先讲自动装配核心思想没变,再补充这些升级点,更显得你知道框架演进而不是只背版本差异。
  • 关键词
    • AutoConfiguration.imports
    • Jakarta
    • Java 17
    • 演进
  • 追问提醒
    • 继续准备“升级 2.x 到 3.x 时常见兼容坑有哪些”。

三、实战场景题(P8+重点)

  1. 事务设计:一个下单流程涉及扣库存、创建订单、扣积分、发通知,如何设计事务边界和传播行为?
  • 回答框架
    • 先区分单服务内强一致动作和跨服务异步动作。
    • 主交易链路内部用本地事务保证订单与库存边界内的一致性。
    • 积分、通知等更适合走事件驱动和最终一致性,而不是全链路大事务。
  • 核心关注点
    • 哪些动作必须强一致,哪些允许异步补偿。
    • REQUIRES_NEW 是否被滥用。
    • 幂等、补偿和重试策略是否完善。
  1. AOP 实战:如何用 AOP 实现一个通用的接口幂等性校验框架?
  • 回答框架
    • 通过注解标识幂等入口。
    • 切面统一提取幂等键、做分布式锁或去重记录校验。
    • 失败时定义统一返回语义和过期策略。
  • 核心关注点
    • 幂等键如何设计,是否和业务主键一致。
    • 锁粒度、超时和异常回滚如何处理。
    • 切面只做横切校验,不要吞掉业务语义。
  1. 自动装配扩展:公司内部有多个微服务共用的配置逻辑,如何设计一个公共 Starter?
  • 回答框架
    • 抽公共能力边界,拆自动配置和依赖聚合模块。
    • 用条件注解控制启用时机,用配置属性暴露可调项。
    • 提供默认埋点、日志、链路和容错能力,同时允许业务按需覆盖。
  • 核心关注点
    • 不能把业务差异硬塞进 Starter。
    • 默认值是否安全,是否容易观测和排障。
    • 版本兼容和升级策略是否清晰。
  1. 性能排查:线上应用启动时间从 30 秒增长到 120 秒,如何排查是哪些自动配置或 Bean 初始化导致?
  • 回答框架
    • 先看启动日志、条件评估报告和关键 Bean 初始化耗时。
    • 再定位是自动配置装太多,还是个别 Bean 初始化做了重操作。
    • 最后通过懒加载、拆初始化动作、延后连接建立等方式优化。
  • 核心关注点
    • 是否抓住启动慢的主因,而不是盲目关功能。
    • 外部依赖连接、扫描范围、配置中心/注册中心初始化是否拖慢启动。
    • 优化后是否影响可用性与预热流程。
  1. 循环依赖治理:接手的老项目有大量循环依赖,如何制定分阶段治理方案?
  • 回答框架
    • 先识别高风险链路和核心 Bean,区分“暂时可运行”和“必须立刻治理”。
    • 短期用 @Lazy 或调整注入方式止血,中期通过职责拆分、事件驱动、门面层重构逐步消除。
    • 最后结合架构规范和扫描工具防止新循环继续引入。
  • 核心关注点
    • 不能把框架兜底当长期方案。
    • 治理顺序要先核心链路、后边缘模块。
    • 是否有自动化手段持续发现新增循环依赖。

四、学习资源推荐

书籍

  • 《Spring 实战》:适合建立 Spring Core、AOP、MVC 基础认知。
  • 《Spring Boot 编程思想》:适合理解自动装配、启动链路和扩展机制。
  • 《Spring 源码深度解析》:适合深入 IOC、AOP、事务等源码主线。

博客/文章

  • Spring 官方文档:最权威,适合查 Core、Boot、MVC、事务边界和版本差异。
  • Spring Boot 官方 Reference:适合核对自动配置、配置属性、条件装配和启动机制。
  • 美团技术团队相关文章:适合补大型业务场景下的 Spring 实践经验。

视频

  • 极客时间《玩转 Spring 全家桶》:适合系统回顾框架全景。
  • B站 Spring 源码解析系列:适合按源码链路做面试深化。

On this page