Spring 系统学习手册
目标:用最短时间掌握 Spring 最重要、最常用、最有实战价值的 80% 内容,达到"能理解、能解释、能上手、能排错、能通过基础到中级面试"的程度。
学习对象:默认你是初学者,但我们会把深度拉到中级工程师能解释原理、能排查问题的层次。
学习原则:先直觉,后定义;先主干,后展开;先能跑,后深挖;先工程常用,后冷门细节。
学习地图
1. 这门学科是干什么的
Spring 是 Java 生态里最核心的企业级开发框架之一。它本质上不是"一个单点技术",而是一套围绕企业应用开发建立起来的编程模型。
它最核心的价值有四个:
- 管对象:把对象的创建、依赖关系、生命周期交给容器统一管理。
- 解耦合:业务代码只关心"我要什么依赖",而不是"我怎么 new 出来它"。
- 统一基础能力:事务、日志、权限、缓存、配置、事件、Web 请求处理都能统一接入。
- 提升工程效率:配合 Spring Boot,可以快速搭建可运行、可配置、可测试、可部署的服务。
一句话理解:
Spring = 用"容器 + 约定 + 扩展点"来组织 Java 企业应用。
2. 它解决什么问题
如果没有 Spring,企业开发会遇到这些典型问题:
| 问题 | 不用 Spring 时会怎样 | Spring 怎么解决 |
|---|---|---|
| 对象依赖复杂 | 代码里到处 new,模块强耦合 |
用 IoC 容器统一创建 Bean |
| 横切逻辑重复 | 每个方法都手写事务、日志、鉴权 | 用 AOP 统一织入 |
| Web 请求处理混乱 | Servlet 层样板代码多 | 用 Spring MVC 统一处理请求 |
| 配置难管理 | 环境差异、硬编码、切环境困难 | 用 application.yml、Profile、配置绑定 |
| 测试困难 | 对象关系复杂,Mock 难写 | 容器化管理,方便替换和注入 |
| 工程规范难统一 | 团队项目结构不一致 | Spring Boot 形成事实标准 |
3. 它和哪些相关技术有关系
Spring 不是孤立存在的,它和下面这些技术高度关联:
| 技术 | 关系 | 学 Spring 时需要掌握到什么程度 |
|---|---|---|
| Java 基础 | Spring 的载体 | 必须吃透:面向对象、接口、泛型、注解、反射基础 |
| Maven / Gradle | 依赖管理和构建 | 必须会基本用法 |
| HTTP / Servlet | Spring MVC 的基础 | 至少理解请求-响应模型 |
| JSON | Web 接口交互常态 | 必须会 |
| JDBC / MyBatis / JPA | 数据访问 | 至少会一种 |
| Spring Boot | Spring 的工程化入口 | 必须掌握 |
| Tomcat / Undertow | Web 容器 | 知道作用即可 |
| AOP / 动态代理 | Spring 核心机制之一 | 必须吃透 |
| 事务 | 企业开发高频能力 | 必须吃透 |
| Redis / MQ / Security | 常见企业扩展 | 先知道,后深入 |
4. 学它之前需要哪些前置知识
建议你至少具备下面这些基础:
- Java 面向对象:类、接口、继承、多态。
- Java 常用语法:集合、异常、泛型、枚举、Lambda 基本理解。
- 注解基础:知道注解是什么、为什么有运行期注解。
- 反射基础:至少知道可以通过反射拿类、方法、字段。
- Maven:会加依赖、跑项目、看依赖树。
- HTTP:知道 GET/POST、请求头、状态码、JSON。
- 数据库基础:会写基本 SQL,知道事务是什么意思。
如果这些还不稳,不用暂停学 Spring,但要边学边补。
5. 真正重要的 20% 核心内容是什么
必须吃透:这部分决定你能不能真正理解 Spring。
- IoC 和 DI:为什么对象要交给容器管理。
- Bean:Bean 是什么、怎么注册、怎么注入、生命周期是什么。
- ApplicationContext:容器到底做了什么。
- AOP 和代理:为什么事务、日志、权限能"自动生效"。
@Transactional:什么时候生效,为什么有时会失效。- Spring MVC:一次 HTTP 请求在 Spring 里怎么走。
- Spring Boot 自动配置:为什么"引个 starter 就能用"。
- 常见排错:Bean 注入失败、配置不生效、事务失效、请求 404/405/415。
如果你把这 8 件事吃透,Spring 面试和项目开发的 80% 问题你都能接住。
6. 哪些内容是初学者容易陷入、但不值得一开始深挖的
先知道,后深入:不要一上来把时间耗在"有技术味但暂时不产出"的地方。
- 过早深挖全部源码。
- XML 配置的所有细节。
- 很少用的 Bean Scope 和冷门扩展点。
- Spring 所有事件机制、资源加载细节。
- WebFlux / Reactor 全家桶。
- Spring Security OAuth2 的完整协议细节。
- Native Image、AOT、GraalVM。
- 各种"背诵型八股",但没有工程场景支撑。
7. 学习本质
学 Spring,不是背注解。
真正要学的是三层东西:
- 思想层:控制反转、约定优于配置、面向接口、扩展点设计。
- 机制层:容器如何建对象图,代理如何织入横切逻辑,Web 请求如何分发。
- 工程层:如何把这些机制落成可维护、可排错、可测试的项目。
如果你只会写 @Service、@Autowired,你只是"会用";
如果你能解释 Bean 生命周期、事务为什么失效、自动配置为什么生效,你才是真正掌握。
8. 设计哲学
Spring 的设计哲学可以概括为:
- 把重复的基础工作收编到框架层。
- 让业务代码面向抽象,不面向创建细节。
- 用元数据驱动行为:注解、配置、条件装配。
- 用扩展点而不是硬编码来支持变化。
业务代码声明依赖
Spring 容器解析配置
创建 Bean
注入依赖
织入事务 / 日志 / 权限
对外提供 Web / Service 能力
学习顺序
正确顺序:先把主干跑通,再补底层;先会解释,再会扩展;先能定位问题,再追源码。
- 先认识 Spring 全家桶,分清 Spring、Spring MVC、Spring Boot 分别干什么。
- 学 IoC / DI / Bean / 容器,这是整个 Spring 的根。
- 学 Bean 生命周期、配置方式、依赖注入规则。
- 学 AOP、动态代理、事务机制。
- 学 Spring MVC 请求处理流程。
- 学 Spring Boot 自动配置、配置文件、Profile、Starter。
- 学工程实践:分层、校验、异常处理、日志、测试、配置管理。
- 学排错:Bean 问题、事务问题、请求问题、配置问题。
- 最后再系统整理面试答法、源码切入点和项目实战。
推荐学习节奏
| 阶段 | 目标 | 结果 |
|---|---|---|
| 第 1 阶段 | 建立整体认知 | 知道 Spring 为什么存在、核心模块是什么 |
| 第 2 阶段 | 理解核心概念 | 会解释 Bean、容器、依赖注入 |
| 第 3 阶段 | 掌握核心机制 | 会解释 AOP、事务、MVC 流程 |
| 第 4 阶段 | 连接到底层原理 | 会解释代理、生命周期、刷新流程 |
| 第 5 阶段 | 落地工程实践 | 能写一个像样的 Spring Boot 服务 |
| 第 6 阶段 | 具备排错能力 | 能定位高频错误 |
| 第 7 阶段 | 形成面试表达 | 能回答基础到中级问题 |
| 第 8 阶段 | 闭环与进阶 | 知道下一步该学什么 |
初学者最容易学偏的地方
- 只记注解,不懂容器。
- 只会 CRUD,不会解释事务和 AOP。
- 直接背源码流程图,但写不出工程代码。
- 把 Spring Boot 当成 Spring 全部。
- 遇到问题只会改配置,不会看日志、断点、定位链路。
必须掌握 vs 知道即可
| 层次 | 必须掌握 | 知道即可 |
|---|---|---|
| 概念 | IoC、DI、Bean、AOP、事务、MVC、Boot 自动配置 | 资源加载、事件广播、冷门 Scope |
| 工程 | 分层、配置、异常处理、校验、日志、测试 | 自定义复杂 Starter、AOT |
| 原理 | 动态代理、Bean 生命周期、事务代理链 | 所有源码类名与调用细节 |
| 面试 | 概念 + 原理 + 场景 + 常见坑 | 冷门扩展点背诵 |
第一阶段:入门认知
1. 学什么
这一阶段只做一件事:建立 Spring 的整体认知。
你要搞清楚:
- Spring 到底是什么,不是什么。
- Spring 解决了企业开发中的哪些痛点。
- Spring、Spring MVC、Spring Boot 之间是什么关系。
- 为什么企业项目几乎都会用 Spring 体系。
2. 为什么重要
如果一开始没有全景视角,后面你会很容易出现两种偏差:
- 把 Spring 当成"注解大全"。
- 把 Spring Boot 当成"黑盒启动器"。
第一阶段的价值,不是写很多代码,而是先搭好脑中的框架。
只有你先知道整个系统的骨架,后面每一个概念才有位置,不会碎片化。
3. 它解决什么实际问题
Spring 最初流行起来,是因为它把企业 Java 开发中最痛的几件事同时解决掉了:
- 对象创建混乱:谁依赖谁、何时创建、如何替换,交给容器统一管理。
- 业务代码被样板逻辑污染:事务、日志、权限这类横切逻辑不用每个方法都手写。
- Web 开发太重:从低层 Servlet 升级到统一的 MVC 编程模型。
- 工程启动太慢:配合 Spring Boot 后,项目可以快速搭起来,配置更收敛。
4. 核心概念定义
先用类比理解。
把 Spring 想成一家"工厂 + 调度中心":
- 你不自己招人组装团队,而是把岗位要求交给工厂。
- 工厂负责招人、分配工位、建立协作关系。
- 你只负责告诉工厂:"订单服务需要库存服务、支付服务。"
技术定义:
| 概念 | 直觉理解 | 技术定义 |
|---|---|---|
| Spring | 企业开发工具箱 | 一套用于构建 Java 企业应用的基础框架体系 |
| IoC | 不自己管对象 | 对象控制权从业务代码转交给容器 |
| DI | 自动配齐依赖 | 容器在创建对象时把依赖注入进去 |
| Bean | 被容器接管的对象 | 由 Spring 管理生命周期和依赖关系的对象 |
| 容器 | 总调度中心 | 通常指 ApplicationContext |
| Spring MVC | Web 请求调度层 | Spring 里的 Web MVC 框架 |
| Spring Boot | 快速工程化入口 | 基于 Spring 的约定式启动与自动配置框架 |
Spring、Spring MVC、Spring Boot 的关系
| 名称 | 关注点 | 你应该怎么理解 |
|---|---|---|
| Spring Framework | 基础框架 | 核心容器、AOP、事务、MVC 等能力来源 |
| Spring MVC | Web 模块 | 处理 HTTP 请求、参数绑定、返回结果 |
| Spring Boot | 工程化加速器 | 帮你更快把 Spring 项目跑起来 |
一句话:
Spring 是底座,Spring MVC 是 Web 模块,Spring Boot 是工程化加速器。
5. 工作流程或运行机制
先看最粗的运行流程:
- 应用启动。
- Spring 创建容器。
- 容器扫描配置类和组件。
- 容器创建 Bean 并注入依赖。
- Web 项目则继续初始化 MVC 相关组件。
- 业务代码通过容器拿到对象开始工作。
如果是 Web 请求,再多一层流程:
- 请求进来。
- DispatcherServlet 接住请求。
- 找到对应的 Controller 方法。
- 做参数绑定。
- 调用 Service。
- 返回 JSON / 页面。
6. 底层原理或设计思想
第一阶段不深挖源码,但你要先知道为什么 Spring 这样设计:
- 企业项目对象多、依赖复杂,用手工管理成本太高,所以需要容器。
- 横切逻辑会污染业务代码,所以需要代理/AOP。
- 大量项目结构类似,所以需要约定优于配置。
- 业务变化大,所以框架要提供扩展点,而不是写死流程。
这背后的设计思想是:
- 分离"对象使用"和"对象创建"。
- 分离"业务逻辑"和"通用能力"。
- 用配置和注解描述意图,用框架执行机制。
7. 一个最小可运行示例
下面这个例子只有一个目的:让你直观看到"对象不是我手动 new 的,而是 Spring 自动装起来的"。
java
package com.example.demo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
private final OrderService orderService;
public DemoApplication(OrderService orderService) {
this.orderService = orderService;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) {
orderService.createOrder();
}
}
@Service
class InventoryService {
public void reserve() {
System.out.println("库存已锁定");
}
}
@Service
class OrderService {
private final InventoryService inventoryService;
public OrderService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
public void createOrder() {
inventoryService.reserve();
System.out.println("订单已创建");
}
}
你会看到输出:
text
库存已锁定
订单已创建
关键观察点:
- 你没有手动
new OrderService()。 - 你也没有手动
new InventoryService()。 - Spring 容器自动创建并组装了它们。
8. 一个真实工程场景
假设你在做一个电商系统,OrderService 依赖:
InventoryService:扣库存。CouponService:校验优惠券。PaymentService:生成支付单。OrderRepository:落库。
如果全都手动 new:
- 依赖关系会扩散到很多地方。
- 替换实现会很痛。
- 测试时难以 Mock。
用 Spring 后:
- 这些服务都交给容器。
OrderService只声明依赖。- 切换实现只改配置或注解。
- 测试时可以替换 Bean。
这就是为什么企业里几乎都用 Spring。
9. 常见误区
- 误区:Spring 就是 Spring Boot。
纠正:Boot 是 Spring 的工程化入口,不是全部。 - 误区:
@Autowired很神秘。
纠正:本质是容器在创建对象时做依赖注入。 - 误区:学 Spring 就是记注解。
纠正:核心是容器、代理、请求流转机制。 - 误区:先看源码最有效。
纠正:没有使用场景和主干认知时,看源码效率很低。
10. 高频面试题
- Spring 是什么?它解决了什么问题?
- IoC 和 DI 是什么关系?
- Spring 和 Spring Boot 有什么区别?
- Spring MVC 是 Spring 的一部分吗?
- 为什么企业项目普遍使用 Spring?
参考答题模板
答"Spring 是什么"时,尽量按这个顺序:
- 定义:Spring 是 Java 企业级开发框架体系。
- 问题:解决对象管理、依赖耦合、事务、Web 开发等问题。
- 机制:核心靠 IoC、AOP、MVC、事务管理。
- 场景:企业中常用 Spring Boot + MVC + 数据访问构建服务。
11. 自测题
- 为什么说 Spring 不是一个单点框架,而是一套体系?
- 如果没有 Spring,手动管理对象会出现什么问题?
- IoC 和 DI 用你自己的话解释一遍。
- Spring、Spring MVC、Spring Boot 各自负责什么?
- 你能不能用"电商订单服务"的例子解释为什么需要 Spring?
12. 学完这一节后我应该能做到什么
学完第一阶段,你至少应该能做到:
- 用 3 分钟讲清楚 Spring 是干什么的。
- 说清 Spring 解决了哪些开发问题。
- 分清 Spring / Spring MVC / Spring Boot 的关系。
- 看懂一个最基础的 Spring Boot 示例,知道容器接管了对象。
第一阶段练习题
- 用你自己的话写一段 150 字说明:为什么 Spring 在企业开发中这么常见。
- 画出下面关系图:Controller -> Service -> Repository,说明为什么这些对象适合交给容器管理。
- 回答这个问题:如果不用 Spring,
OrderService依赖 5 个服务时,你预计会出现哪些维护问题? - 自己新建一个最小 Spring Boot 项目,把上面的
OrderService跑起来。
第二阶段:核心概念
1. 学什么
这一阶段学 Spring 的根:
- Bean 是什么。
- 容器是什么,
BeanFactory和ApplicationContext是什么关系。 - 依赖注入怎么发生。
- Bean 怎么注册。
- 常见作用域、注入方式和配置方式。
2. 为什么重要
Spring 大部分能力都建立在"Bean 被容器接管"这件事上。
如果这一阶段没吃透,后面你会出现这些问题:
- 看不懂为什么某个类能被注入,另一个不行。
- 不知道 Bean 冲突时为什么会报错。
- 事务、AOP 明明写了注解却不生效,不知道从哪查。
3. 它解决什么实际问题
核心概念阶段解决的是"对象如何组织起来"的问题。
没有容器时:
- 依赖关系分散在代码各处。
- 第三方组件很难纳入统一管理。
- 生命周期无法统一控制。
- 测试替换困难。
有了容器后:
- 对象创建统一入口。
- 依赖注入统一规则。
- 初始化和销毁有统一钩子。
- 切换实现和替换测试桩更容易。
4. 核心概念定义
Bean
Bean 就是交给 Spring 容器管理的对象。
不是所有 Java 对象都是 Bean,只有被 Spring 注册到容器里的对象才是 Bean。
容器
容器是 Spring 管理 Bean 的运行时环境,常用接口是 ApplicationContext。
BeanDefinition
BeanDefinition 可以理解为"Bean 的说明书",里面描述了:
- 这个 Bean 对应哪个类。
- 作用域是什么。
- 初始化方法是什么。
- 依赖是什么。
依赖注入
依赖注入就是容器在创建对象时,把它需要的其他对象传进去。
作用域
| Scope | 含义 | 常见场景 |
|---|---|---|
| singleton | 容器内单例 | 默认,大多数 Service |
| prototype | 每次获取新对象 | 少数状态对象 |
| request | 每次 HTTP 请求一个 | Web 场景 |
| session | 每个会话一个 | 很少直接用 |
BeanFactory vs ApplicationContext
| 对比项 | BeanFactory | ApplicationContext |
|---|---|---|
| 定位 | 基础容器 | 更完整的高级容器 |
| 功能 | 基本 Bean 获取 | 国际化、事件、AOP、环境、自动注册等 |
| 使用频率 | 理解原理时常见 | 工程里几乎总是用它 |
@Component vs @Bean
| 注解 | 用在哪 | 典型场景 |
|---|---|---|
@Component |
标在类上 | 自己写的组件 |
@Service / @Repository / @Controller |
@Component 的语义化变种 |
分层标识 |
@Bean |
标在方法上 | 第三方类、需要手工定制的 Bean |
注入方式对比
| 方式 | 推荐度 | 原因 |
|---|---|---|
| 构造器注入 | 最推荐 | 依赖明确、利于测试、便于不可变设计 |
| Setter 注入 | 可用 | 适合可选依赖 |
| 字段注入 | 不推荐 | 不利于测试,依赖不显式 |
5. 工作流程或运行机制
从"类"到"能用的 Bean",大致是这个过程:
- Spring 扫描配置类或组件类。
- 解析出 BeanDefinition。
- 容器创建 Bean 实例。
- 解析并注入依赖。
- 调用初始化回调。
- 把 Bean 放入单例池。
- 业务代码使用它。
可以把它理解成:
类只是候选人,BeanDefinition 是档案,真正进容器并完成装配后,才变成可工作的 Bean。
6. 底层原理或设计思想
Spring 没有发明"对象"本身,它做的是"对象图管理"。
设计思想有三个关键点:
- 元数据驱动:通过注解、配置类、
@Bean方法告诉容器该怎么装配。 - 延迟复杂性:业务代码不需要知道对象怎么创建。
- 统一生命周期:创建、注入、初始化、销毁都有固定阶段。
这也是为什么 Spring 能把事务、AOP、事件、配置都统一挂到容器上。
7. 一个最小可运行示例
下面这个例子同时演示:
- 多个实现类。
@Qualifier指定注入。@Bean注册第三方对象。
java
package com.example.demo;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
private final CheckoutService checkoutService;
public DemoApplication(CheckoutService checkoutService) {
this.checkoutService = checkoutService;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) {
System.out.println(checkoutService.checkout());
}
}
interface PayService {
String pay();
}
@Component("alipayService")
class AlipayService implements PayService {
@Override
public String pay() {
return "使用支付宝支付";
}
}
@Component("wechatPayService")
class WechatPayService implements PayService {
@Override
public String pay() {
return "使用微信支付";
}
}
@Service
class CheckoutService {
private final PayService payService;
private final IdGenerator idGenerator;
public CheckoutService(
@Qualifier("alipayService") PayService payService,
IdGenerator idGenerator) {
this.payService = payService;
this.idGenerator = idGenerator;
}
public String checkout() {
return idGenerator.nextId() + " -> " + payService.pay();
}
}
class IdGenerator {
public String nextId() {
return "ORDER-" + System.currentTimeMillis();
}
}
@Configuration
class AppConfig {
@Bean
public IdGenerator idGenerator() {
return new IdGenerator();
}
}
8. 一个真实工程场景
支付网关通常有多种实现:
- 支付宝
- 微信支付
- 银联
- 测试环境模拟支付
如果不使用容器和接口抽象,切换实现会改动很多业务代码。
合理做法:
- 定义
PayService接口。 - 各个支付方式各自实现。
- 用 Spring 管理这些实现。
- 通过
@Qualifier、@Primary、配置项等方式选择具体实现。
9. 常见误区
- 误区:Bean 就是 Java 类。
纠正:Bean 是"被容器管理的对象",类只是定义。 - 误区:单例 Bean 一定线程安全。
纠正:单例只代表实例数量,不代表内部状态安全。 - 误区:字段注入最省事,所以最好。
纠正:构造器注入更清晰、更利于测试。 - 误区:
@Component和@Bean完全一样。
纠正:本质都是注册 Bean,但使用场景不同。
10. 高频面试题
- 什么是 Bean?
- BeanFactory 和 ApplicationContext 有什么区别?
- Spring 中常见的 Bean 注入方式有哪些?推荐哪种?
@Component和@Bean的区别是什么?- 单例 Bean 是线程安全的吗?
11. 自测题
- 为什么说"Bean != 普通对象"?
- 你如何向别人解释
@Bean的使用场景? - 为什么构造器注入比字段注入更推荐?
- 如果容器里有两个
PayService,Spring 怎么知道注入哪个? singleton和prototype在工程上的差异是什么?
12. 学完这一节后我应该能做到什么
- 解释 Bean、容器、依赖注入的含义。
- 能用
@Component、@Bean、@Qualifier完成基本装配。 - 理解为什么推荐构造器注入。
- 知道 Bean 作用域的基本差异。
第三阶段:核心机制
1. 学什么
这一阶段学习 Spring 最容易在项目和面试中出现的问题源头:
- Bean 生命周期。
- AOP 机制。
- 动态代理。
- 事务机制。
- Spring MVC 请求处理机制。
2. 为什么重要
这一阶段决定你是否只是"会写注解",还是"真正理解框架行为"。
很多高频故障都来自这里:
- 事务失效。
- AOP 不生效。
- Controller 能调,事务却没开。
- 明明加了组件注解,启动后行为不符合预期。
3. 它解决什么实际问题
AOP 解决什么
把日志、事务、权限、监控这类"很多地方都要做"的逻辑,从业务代码中剥离出来。
事务解决什么
保证一组数据库操作要么全成功,要么全失败。
MVC 解决什么
把 HTTP 请求转成统一的 Controller 调用模型,不再直接手写 Servlet 样板代码。
4. 核心概念定义
| 概念 | 定义 |
|---|---|
| Bean 生命周期 | Bean 从创建、注入、初始化到销毁的完整过程 |
| AOP | 面向切面编程,把横切逻辑统一织入目标方法 |
| 代理 | 用代理对象包一层,在调用前后增加附加行为 |
| Join Point | 可被切入的执行点,Spring AOP 里主要是方法执行 |
| Pointcut | 切点,定义拦截哪些方法 |
| Advice | 通知,定义拦截后做什么 |
@Transactional |
用声明式事务管理方法执行边界 |
| DispatcherServlet | Spring MVC 的前端控制器 |
JDK 动态代理 vs CGLIB
| 对比项 | JDK 动态代理 | CGLIB |
|---|---|---|
| 代理对象要求 | 目标类要有接口 | 不要求接口 |
| 原理 | 代理接口 | 生成子类 |
| 常见场景 | 面向接口编程 | 无接口类代理 |
5. 工作流程或运行机制
Bean 生命周期简化版
- 实例化 Bean。
- 注入依赖。
- 执行
Aware回调。 - 执行
BeanPostProcessor前置逻辑。 - 执行初始化方法。
- 执行
BeanPostProcessor后置逻辑。 - Bean 可用。
- 容器关闭时执行销毁逻辑。
事务执行流程
- 调用代理对象的方法。
- 事务拦截器判断是否需要开启事务。
- 开启事务。
- 调用目标方法。
- 正常则提交,异常则回滚。
MVC 请求流程
Service Controller HandlerAdapter HandlerMapping DispatcherServlet Client Service Controller HandlerAdapter HandlerMapping DispatcherServlet Client HTTP Request 查找处理器 返回 Handler 适配调用 调用 Controller 方法 执行业务 返回结果 对象 / 视图 JSON / 页面
6. 底层原理或设计思想
为什么事务和日志能"自动生效"
因为你调用的通常不是目标对象本体,而是 Spring 给你包出来的代理对象。
代理做的事:
- 在调用前加逻辑。
- 调目标方法。
- 在调用后处理提交、回滚、日志、埋点等。
为什么 AOP 用代理
因为这样不用改业务类源码,就能在方法调用前后附加行为,符合开闭原则。
为什么 MVC 用 DispatcherServlet
因为它能把所有请求先统一接住,再按规则转发给不同 Controller,这样 Web 层行为可统一扩展。
7. 一个最小可运行示例
下面这个例子演示:
@Aspect做日志。@Transactional做事务边界。
java
package com.example.demo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
@SpringBootApplication
@EnableTransactionManagement
public class DemoApplication implements CommandLineRunner {
private final OrderService orderService;
public DemoApplication(OrderService orderService) {
this.orderService = orderService;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) {
orderService.submitOrder();
}
}
@Service
class OrderService {
@Transactional
public void submitOrder() {
System.out.println("创建订单");
System.out.println("扣减库存");
}
}
@Aspect
@Component
class LogAspect {
@Around("execution(* com.example.demo.OrderService.*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法开始: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed();
System.out.println("方法结束: " + joinPoint.getSignature().getName());
return result;
}
}
8. 一个真实工程场景
下单流程通常包含:
- 创建订单记录。
- 扣减库存。
- 锁定优惠券。
- 记录操作日志。
这里最合适的设计是:
- 用
@Transactional包裹 Service 方法,保证一致性。 - 用 AOP 记录操作日志或接口耗时。
- Controller 只负责入参和返回,不直接写事务逻辑。
9. 常见误区
- 误区:AOP 是在源码层面改代码。
纠正:Spring AOP 默认是运行期代理,不是直接改源码。 - 误区:
@Transactional标了就一定生效。
纠正:自调用、非 public 方法、异常被吞掉等都可能导致失效。 - 误区:Controller 上加事务也很好。
纠正:事务边界更适合放在 Service 层。 - 误区:AOP 能拦截所有东西。
纠正:Spring AOP 主要基于方法执行点。
10. 高频面试题
- Spring AOP 是怎么实现的?
- JDK 动态代理和 CGLIB 有什么区别?
@Transactional的底层原理是什么?- Spring 事务为什么会失效?
- 一次 Spring MVC 请求从进入到返回经历了什么?
11. 自测题
- 为什么说事务本质上依赖代理?
- 为什么同类内部方法互调可能导致事务失效?
- DispatcherServlet 在 MVC 中起什么作用?
- AOP 解决的到底是什么类型的问题?
- 你能按步骤说出一个方法被事务代理包裹后的执行过程吗?
12. 学完这一节后我应该能做到什么
- 解释 Spring AOP 的基本实现思路。
- 解释
@Transactional为什么有效、为什么有时失效。 - 能说清 MVC 请求的主流程。
- 能用"代理"解释事务、日志、权限等横切能力。
第四阶段:底层原理
1. 学什么
这一阶段把前面"会用"和"会解释"连接起来,重点理解:
- Spring 容器启动时干了什么。
refresh()大致在做什么。- BeanDefinition 怎么来的。
BeanPostProcessor、BeanFactoryPostProcessor做什么。- 自动代理创建的大致流程。
2. 为什么重要
这是从"会用 Spring"跨到"能做中级解释和排错"的关键阶段。
你不需要一上来把全部源码背下来,但至少要知道主链路。
3. 它解决什么实际问题
底层原理阶段不是为了炫技,而是为了解决下面几类真实问题:
- 启动失败时,你知道卡在哪一层。
- Bean 为什么没注册进去,你知道去看扫描、条件装配、配置类解析。
- 事务代理为什么没生效,你知道它和代理创建链有关。
- 想自定义扩展时,你知道该挂在哪个扩展点。
4. 核心概念定义
| 概念 | 作用 |
|---|---|
refresh() |
容器启动核心流程 |
| BeanDefinition | Bean 的描述信息 |
BeanFactoryPostProcessor |
在 Bean 实例化前,修改 Bean 定义 |
BeanPostProcessor |
在 Bean 实例化后、初始化前后做处理 |
InstantiationAwareBeanPostProcessor |
更早阶段介入实例化流程 |
| Auto Proxy Creator | 为符合条件的 Bean 创建代理 |
5. 工作流程或运行机制
Spring 容器启动主流程(简化)
- 创建
ApplicationContext。 - 加载配置类、扫描组件。
- 注册 BeanDefinition。
- 执行
BeanFactoryPostProcessor。 - 注册
BeanPostProcessor。 - 实例化非懒加载单例 Bean。
- 完成容器刷新。
Bean 被代理的大致流程
- Spring 先按正常流程创建 Bean。
- 某个后置处理器判断这个 Bean 是否需要代理。
- 如果需要,就生成代理对象替代原对象放入容器。
- 后续你拿到的其实是代理对象。
6. 底层原理或设计思想
Spring 的底层设计非常强调"分阶段"和"可插拔":
- 先注册定义,再创建对象。
- 先给扩展点机会,再正式实例化。
- 通过后置处理器,在不入侵业务代码的前提下增强行为。
这就是为什么 Spring 能不断叠加功能,但业务代码改动很少。
一个很重要的理解
Spring 核心不是"注解驱动",而是"元数据 + 生命周期阶段 + 扩展点"驱动。
注解只是告诉 Spring "我要什么",真正工作的仍然是容器生命周期和各种处理器。
7. 一个最小可运行示例
下面用 BeanPostProcessor 看看 Bean 初始化前后发生了什么:
java
package com.example.demo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public UserService userService() {
return new UserService();
}
}
class UserService {
public UserService() {
System.out.println("构造 UserService");
}
}
@Component
class TraceBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化前: " + beanName);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("userService".equals(beanName)) {
System.out.println("初始化后: " + beanName);
}
return bean;
}
}
你会看到类似输出:
text
构造 UserService
初始化前: userService
初始化后: userService
这能帮你直观看到:Spring 对 Bean 的处理不是"一步到位",而是分阶段进行的。
8. 一个真实工程场景
比如你希望所有带某个注解的 Service 在启动时自动注册审计信息,或者统一包一层代理记录调用耗时。
这时通常不是去改每个类,而是:
- 写一个后置处理器。
- 在 Bean 初始化阶段统一扫描。
- 对符合条件的 Bean 做增强。
这就是 Spring 扩展点的工程价值。
9. 常见误区
- 误区:Spring 一启动就把所有类都变成 Bean。
纠正:只有被扫描、被配置、符合条件的类才会注册。 - 误区:注解是 JVM 自动帮你注入对象。
纠正:注解只是元数据,真正执行装配的是 Spring。 - 误区:代理对象和原对象完全没差别。
纠正:很多行为差异都来自你拿到的是代理。
10. 高频面试题
- Spring 容器启动大致流程是什么?
- 什么是 BeanDefinition?
- BeanFactoryPostProcessor 和 BeanPostProcessor 的区别是什么?
- Spring 是在哪个阶段创建代理的?
- 为什么说注解只是元数据?
11. 自测题
refresh()这个词在 Spring 里大概意味着什么?- 为什么 Spring 要区分"Bean 定义"和"Bean 实例"?
- 如果要在 Bean 创建前修改其配置,你应该想到哪个扩展点?
- 代理对象通常是在什么阶段替换掉原始 Bean 的?
12. 学完这一节后我应该能做到什么
- 能从"容器启动 -> 注册定义 -> 实例化 -> 后处理 -> 代理"这个链路解释 Spring。
- 知道常见后置处理器的职责边界。
- 遇到容器启动问题时,知道去怀疑扫描、定义注册、条件装配和代理链。
第五阶段:工程实践
1. 学什么
这一阶段我们不再停留在知识点,而是看企业里通常怎么用 Spring:
- Spring Boot 项目结构。
- Controller / Service / Repository 分层。
- 配置文件、Profile、配置绑定。
- 参数校验、统一异常处理、日志。
- 事务边界、接口设计、DTO/VO。
- 测试基础。
2. 为什么重要
面试和真实开发都不看你会不会"单点注解",而看你能不能把一个服务写得像工程。
工程实践是把 Spring 从"概念"变成"产能"的阶段。
3. 它解决什么实际问题
工程实践阶段解决的问题包括:
- 项目结构混乱。
- 配置散落、环境切换困难。
- Controller 逻辑太重。
- 参数校验和异常处理不统一。
- 日志不可追踪,问题难排查。
4. 核心概念定义
| 概念 | 含义 |
|---|---|
| Starter | 帮你收敛依赖和自动配置的一组依赖包 |
| Auto Configuration | 根据条件自动装配组件 |
| Profile | 多环境配置切换机制 |
@ConfigurationProperties |
把配置绑定到对象 |
| DTO | 面向接口传输的数据对象 |
| VO | 面向展示或返回的数据对象 |
@Valid / @Validated |
参数校验 |
@RestControllerAdvice |
全局异常处理 |
企业常见项目结构
text
src/main/java
├── controller
├── service
├── service/impl
├── repository 或 mapper
├── domain 或 entity
├── dto
├── vo
├── config
├── common
└── Application.java
5. 工作流程或运行机制
一个标准的接口请求大致这样流转:
- 请求进入 Controller。
@RequestBody绑定参数。@Valid执行参数校验。- Controller 调用 Service。
- Service 开启事务、处理业务、访问数据库。
- 返回 VO 或统一响应对象。
- 发生异常时由全局异常处理器统一接住。
6. 底层原理或设计思想
工程实践阶段最重要的思想有四个:
- 分层不是为了好看,而是为了职责单一、便于测试和扩展。
- 配置外置化不是为了"高级",而是为了环境切换和运维协作。
- DTO / VO 分离不是为了形式主义,而是为了隔离内部模型和外部协议。
- 异常统一处理不是为了省代码,而是为了稳定输出和统一观测。
过滤器、拦截器、AOP 对比
| 能力 | 作用层 | 常见用途 |
|---|---|---|
| Filter | Servlet 层 | 编码、跨域、链路追踪入口 |
| Interceptor | MVC 层 | 登录态、权限、请求日志 |
| AOP | 方法调用层 | 事务、埋点、审计、统一增强 |
7. 一个最小可运行示例
下面是一个更贴近真实项目的 REST 接口最小骨架:
java
package com.example.demo;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestController
@RequestMapping("/orders")
class OrderController {
private final OrderService orderService;
OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public OrderVO create(@Valid @RequestBody CreateOrderRequest request) {
return orderService.create(request);
}
}
record CreateOrderRequest(
@NotBlank(message = "商品编码不能为空") String productCode,
@Min(value = 1, message = "购买数量必须大于 0") int count) {
}
record OrderVO(String orderNo, String status) {
}
@org.springframework.stereotype.Service
class OrderService {
public OrderVO create(CreateOrderRequest request) {
return new OrderVO("ORDER-1001", "CREATED");
}
}
@RestControllerAdvice
class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public String handle(MethodArgumentNotValidException ex) {
return ex.getBindingResult().getFieldError().getDefaultMessage();
}
}
对应配置文件:
yaml
spring:
application:
name: spring-learning-demo
server:
port: 8080
8. 一个真实工程场景
典型的订单服务一般至少要做到:
- 下单接口参数校验。
- Service 层事务控制。
- 异常统一转换。
- 多环境配置切换。
- 日志中带请求标识。
- 和数据库、Redis、MQ 等组件整合。
企业里通常不是"裸 Spring",而是:
- Spring Boot 负责启动和装配。
- Spring MVC 负责接口层。
- Spring Transaction 负责事务。
- Spring + MyBatis/JPA 负责数据层。
9. 常见误区
- 误区:Controller 可以写全部业务逻辑。
纠正:Controller 负责输入输出,不应承载核心业务。 - 误区:数据库实体直接当接口返回对象。
纠正:最好通过 DTO / VO 隔离内部实现。 - 误区:所有配置都写在一个
application.yml。
纠正:应结合 Profile 做多环境拆分。 - 误区:所有异常都在 Controller 里 try-catch。
纠正:优先使用全局异常处理。
10. 高频面试题
- Spring Boot 为什么能做到开箱即用?
@ConfigurationProperties和@Value有什么区别?- 为什么要做统一异常处理?
- 为什么事务一般放在 Service 层?
- Filter、Interceptor、AOP 有什么区别?
11. 自测题
- 为什么企业项目常见分层结构?
- DTO 和 Entity 混用会带来什么问题?
- 你会把参数校验放在哪一层?为什么?
- 多环境配置如何组织更合理?
- 为什么统一异常处理比到处 try-catch 更好?
12. 学完这一节后我应该能做到什么
- 能搭一个结构合理的 Spring Boot 服务。
- 能写 Controller、Service、异常处理、配置文件。
- 知道企业项目里 Spring 各模块如何组合使用。
- 能说出分层、校验、事务、日志、配置管理的工程价值。
第六阶段:常见问题与排错
1. 学什么
这一阶段学习的是实战里最值钱的一项能力:排错。
重点包括:
- Bean 注入失败。
- Bean 冲突。
- 事务失效。
- 请求 404 / 405 / 415 / 400。
- 配置不生效。
- 启动慢或启动失败。
2. 为什么重要
很多开发者不是不会写代码,而是不会定位问题。
Spring 体系一旦规模起来,问题往往不是语法错误,而是配置、装配、代理、链路上的错位。
3. 它解决什么实际问题
这一阶段解决的不是"概念记忆",而是这些具体场景:
NoSuchBeanDefinitionExceptionNoUniqueBeanDefinitionExceptionBeanCurrentlyInCreationException@Transactional不回滚- 接口明明写了却 404
- 参数提交了却报 415 / 400
- 配置改了却像没生效
4. 核心概念定义
| 错误/概念 | 含义 |
|---|---|
NoSuchBeanDefinitionException |
找不到要注入的 Bean |
NoUniqueBeanDefinitionException |
找到多个同类型 Bean,不知道选哪个 |
| 循环依赖 | A 依赖 B,B 又依赖 A |
| 事务失效 | 标了事务但没开启、没回滚或没传播 |
| 415 | 请求体类型或 Content-Type 不匹配 |
| 400 | 请求参数绑定失败 |
5. 工作流程或运行机制
推荐排错顺序:
- 先看完整异常栈。
- 找最根因的异常,而不是只看最外层报错。
- 判断问题落在哪一层:容器、MVC、事务、配置、数据访问。
- 复现最小场景。
- 验证假设,而不是盲改配置。
一张高频排错表
| 症状 | 常见根因 | 先查什么 | 怎么改 |
|---|---|---|---|
| 注入失败 | 没扫描到、没注册 Bean | 包路径、注解、配置类 | 调整扫描路径或注册方式 |
| 注入冲突 | 同类型多个 Bean | 是否有 @Primary / @Qualifier |
指定注入目标 |
| 事务不回滚 | 自调用、异常被吞、异常类型不对 | 调用路径、异常处理 | 从代理入口调用,明确回滚规则 |
| 404 | 映射没注册、路径写错 | 启动日志、@RequestMapping |
修正映射 |
| 415 | Content-Type 不对 |
请求头和入参注解 | 发送 JSON 并加 application/json |
| 配置不生效 | Profile 错、前缀错、未绑定 | 激活环境、配置键名 | 校正配置与绑定类 |
6. 底层原理或设计思想
真正高效的排错,不是"经验玄学",而是把问题映射回机制:
- Bean 问题,本质是容器装配问题。
- 事务问题,本质是代理和调用路径问题。
- MVC 问题,本质是请求映射和参数绑定问题。
- 配置问题,本质是环境加载和绑定规则问题。
所以排错时一定要问:
- 这个问题发生在哪层?
- 这一层的核心机制是什么?
- 哪个前提没满足?
7. 一个最小可运行示例
下面是一个最常见的"注入失败"示例:
java
package com.demo.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// 假设这个类放到了 com.other.service 包下,并且没有被扫描到
@Service
class UserService {
}
如果 UserService 不在启动类所在包及其子包下,又没显式 @ComponentScan,就可能注入失败。
修复方式之一:
java
@SpringBootApplication(scanBasePackages = {"com.demo.app", "com.other.service"})
public class DemoApplication {
}
8. 一个真实工程场景
真实项目里最常见的事务问题之一:
OrderService.createOrder() 里调用同类的 saveAudit(),而 saveAudit() 上标了 @Transactional,结果事务没生效。
原因:
- 同类内部调用没有经过 Spring 代理。
- 事务增强是挂在代理对象上的。
正确做法通常有三种:
- 把事务方法拆到另一个 Bean 中。
- 通过代理对象调用。
- 重新设计事务边界。
9. 常见误区
- 误区:只要报错就先怀疑版本冲突。
纠正:先看具体异常链和问题层级。 - 误区:事务不生效就把注解到处加。
纠正:先看调用是否经过代理。 - 误区:出现循环依赖就直接
@Lazy。
纠正:先判断是不是设计有问题。 - 误区:参数绑定失败就改 Controller 签名碰运气。
纠正:先确认请求格式、注解和字段名是否一致。
10. 高频面试题
- Spring 中 Bean 注入失败通常怎么排查?
- 为什么
@Transactional会失效? - 你如何排查一个接口 404?
NoSuchBeanDefinitionException和NoUniqueBeanDefinitionException有什么区别?- 你如何定位配置不生效的问题?
11. 自测题
- 如果启动时报找不到 Bean,你第一步看什么?
- 如果事务没有回滚,你会依次检查哪几项?
- 415 和 400 在 Spring MVC 中通常分别意味着什么?
- 如果某个配置项你确认写了,但程序没读到,可能的原因有哪些?
12. 学完这一节后我应该能做到什么
- 能按机制分层定位 Spring 问题。
- 能独立处理大部分常见装配、事务、MVC、配置类错误。
- 不再靠"试错式改注解",而是有步骤地排查。
第七阶段:面试与评估
1. 学什么
这一阶段学的不是新知识,而是把前面的知识变成"能说出来、说得清楚、说得像做过项目"。
重点包括:
- 面试高频题的答法结构。
- 如何把原理题和项目经历连起来。
- 如何避免只背八股。
2. 为什么重要
面试官要的不是"你背过名词",而是:
- 你是否真的理解。
- 你是否在项目里用过。
- 你是否知道常见问题与权衡。
3. 它解决什么实际问题
面试中经常出现这些困境:
- 懂一点,但说不成体系。
- 会用,却解释不出原理。
- 会背概念,但一问项目场景就断。
这一阶段就是把知识组织成可表达、可评估的形式。
4. 核心概念定义
面试回答建议遵循一个固定模板:
- 定义:它是什么。
- 目标:它解决什么问题。
- 机制:它怎么实现。
- 场景:项目里怎么用。
- 坑点:什么时候会失效或踩坑。
这个模板适用于回答:
- IoC
- AOP
- 事务
- MVC
- Spring Boot 自动配置
5. 工作流程或运行机制
一道原理题怎么答
以"Spring 事务是怎么实现的"为例:
- 先说定义:Spring 常用声明式事务管理。
- 再说问题:避免手写事务模板代码。
- 再说机制:基于 AOP/代理,在方法前后开启、提交、回滚事务。
- 再说前提:必须经过代理调用。
- 再说坑:自调用、异常被吞、方法可见性问题。
- 再说场景:订单、库存、优惠券这类一致性操作。
6. 底层原理或设计思想
好的面试回答有两个设计原则:
- 不只讲"是什么",还讲"为什么这样设计"。
- 不只讲"理论",还讲"实际项目怎么用、哪里会出问题"。
面试官最喜欢追问的不是定义本身,而是:
- 为什么要这样设计?
- 这样设计的代价是什么?
- 你在项目里怎么用?
- 你踩过什么坑?
7. 一个最小可运行示例
下面这段代码适合你拿来练"事务 + AOP + 分层"的面试表达:
java
@RestController
class OrderController {
private final OrderService orderService;
OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping("/orders")
public String create() {
orderService.createOrder();
return "ok";
}
}
@Service
class OrderService {
@Transactional
public void createOrder() {
// 写订单
// 扣库存
}
}
你可以围绕它回答:
- Spring 如何把 Controller 注入到容器。
- 事务为什么要放 Service。
@Transactional为什么依赖代理。- 一次请求如何从 Controller 走到 Service。
8. 一个真实工程场景
如果面试官问你:"你们项目里 Spring 怎么用的?"
一个像样的回答可以是:
- 用 Spring Boot 搭建服务。
- 用 Spring MVC 对外提供 REST 接口。
- 用 Spring 的事务管理保证下单、库存扣减的一致性。
- 用
@ConfigurationProperties管理多环境配置。 - 用全局异常处理、参数校验和日志链路保证接口稳定性。
- 遇到事务失效和 Bean 冲突时,能从代理和容器装配角度排查。
9. 常见误区
- 误区:只背标准答案。
纠正:必须把答案挂到项目场景上。 - 误区:原理题回答越细越好。
纠正:先答主干,再根据追问展开。 - 误区:只讲优点,不讲坑。
纠正:中级面试更看重边界和问题意识。
10. 高频面试题
- Spring 的核心思想是什么?
- IoC 和 DI 有什么区别与联系?
- Bean 生命周期有哪些阶段?
- Spring AOP 和代理是什么关系?
- Spring 事务底层如何实现?
- Spring Boot 自动配置原理是什么?
- Spring MVC 的请求流程是什么?
- 为什么事务通常写在 Service 而不是 Controller?
11. 自测题
- 你能否用"定义 + 问题 + 机制 + 场景 + 坑"回答 IoC?
- 你能否在 2 分钟内讲清事务底层原理?
- 如果面试官追问"为什么自调用会导致事务失效",你能解释代理链吗?
- 如果面试官让你说一个项目中的 Spring 排错案例,你能讲出来吗?
12. 学完这一节后我应该能做到什么
- 用结构化方式回答 Spring 核心问题。
- 把 Spring 原理和项目经验连起来。
- 对基础到中级面试有较强应对能力。
第八阶段:学习总结与知识闭环
1. 学什么
最后一阶段做三件事:
- 把 Spring 知识串成一张网。
- 明确哪些内容已经够用,哪些值得继续深入。
- 制定下一步进阶路线。
2. 为什么重要
很多人学完一堆知识点,却没有闭环:
- 不知道什么是重点。
- 不知道该怎么继续练。
- 不知道怎么把知识迁移到项目。
3. 它解决什么实际问题
这一阶段解决的是"学完就散"的问题。
闭环的目标是:
- 概念能解释。
- 代码能写。
- 故障能查。
- 面试能答。
- 项目能落。
4. 核心概念定义
学习闭环
学习闭环 = 概念 -> 原理 -> 示例 -> 实战 -> 排错 -> 复盘 -> 再表达。
学习本质
Spring 真正要建立的是下面这条主线:
- 容器管理对象。
- 代理增强行为。
- MVC 组织请求。
- Boot 收敛工程。
- 工程规范让项目可维护。
5. 工作流程或运行机制
推荐你把知识复盘成下面这张图:
IoC / DI
Bean 生命周期
AOP / 代理
事务管理
Spring MVC
Spring Boot 工程化
工程实践与排错
面试表达与项目实战
6. 底层原理或设计思想
Spring 的学习不要停在"功能点"层面,而要逐步建立下面三种意识:
- 容器意识:先想对象是否该交给容器。
- 代理意识:先想增强逻辑是否应交给 AOP。
- 分层意识:先想职责边界是否合理。
真正的中级工程师,不是会背更多名词,而是做设计和排错时能自然调用这些意识。
7. 一个最小可运行示例
如果你想验证自己是否真的形成闭环,建议自己做一个最小项目:
- 提供一个
POST /orders接口。 - 校验参数。
- 调用 Service 创建订单。
- 用事务包裹业务。
- 用全局异常处理返回错误。
- 用配置文件区分 dev / test。
只要这个项目你能独立从 0 写出来,并解释每一层为什么这样写,你就已经完成了非常关键的一轮闭环。
8. 一个真实工程场景
企业里通常不会单独问"你会不会 Spring",而是看你能不能在一个服务里合理使用它:
- 结构清楚。
- 接口规范。
- 事务边界正确。
- 异常处理统一。
- 配置可切环境。
- 问题能快速定位。
所以学 Spring 的最终目标,不是"学懂框架",而是"能用框架交付业务"。
9. 常见误区
- 误区:学完课程就算掌握了。
纠正:必须经过编码、排错、讲解三轮验证。 - 误区:中级就是背更多源码。
纠正:中级更重要的是能解释、能设计、能定位。 - 误区:Spring 学完就结束了。
纠正:后面还有数据访问、缓存、消息、权限、微服务等扩展。
10. 高频面试题
- Spring 体系里你认为最核心的思想是什么?
- 你是如何理解"容器"和"代理"这两个关键抽象的?
- 如果让你从 0 搭一个 Spring Boot 服务,你会怎么组织结构?
- 你认为初学者最容易踩的 Spring 坑是什么?
11. 自测题
- 你能否画出 Spring 的知识主线图?
- 你能否用一句话解释 IoC、AOP、MVC、Boot 各自位置?
- 你能否设计一个小项目,覆盖 Bean、事务、MVC、配置、异常处理?
12. 学完这一节后我应该能做到什么
- 知道 Spring 的学习闭环是什么。
- 明确下一步进阶方向。
- 具备把 Spring 知识迁移到真实项目的能力。
学习评估体系
1. 入门达标标准
满足下面 5 条,算入门达标:
- 能说清 Spring、Spring MVC、Spring Boot 的关系。
- 能解释 IoC、DI、Bean、容器的基本含义。
- 能独立跑起一个最小 Spring Boot 项目。
- 能写出一个简单的 Controller + Service 示例。
- 知道事务、AOP、MVC 分别大致解决什么问题。
2. 初级达标标准
满足下面 6 条,算初级达标:
- 能用构造器注入完成多层依赖装配。
- 能解释 Bean 生命周期主流程。
- 能使用
@Transactional,并知道常见失效场景。 - 能完成参数校验、全局异常处理、配置绑定。
- 能排查常见 Bean 注入和 MVC 映射问题。
- 能结合项目说明 Spring 在工程中的使用方式。
3. 中级达标标准
满足下面 7 条,算中级达标:
- 能解释 Spring 容器启动主流程。
- 能解释 AOP 与代理的关系,以及事务如何基于代理生效。
- 能说出
BeanPostProcessor、BeanFactoryPostProcessor的职责差异。 - 能设计合理的事务边界、分层结构和配置组织方式。
- 能针对事务失效、Bean 冲突、配置不生效给出定位路径。
- 能把原理题和项目场景自然结合。
- 能独立完成一个中小型 Spring Boot 服务原型。
4. 一套自测题
基础题
- 什么是 IoC?它为什么能降低耦合?
- Bean 和普通对象的区别是什么?
@Component和@Bean的差别是什么?- Spring MVC 中 DispatcherServlet 的作用是什么?
- Spring Boot 为什么能简化开发?
进阶题
- Bean 生命周期有哪些关键阶段?
- Spring AOP 默认是如何实现的?
@Transactional为什么依赖代理?- 同类内部方法调用为什么可能导致事务失效?
BeanFactoryPostProcessor和BeanPostProcessor有什么区别?
排错题
- 出现
NoSuchBeanDefinitionException时你如何排查? - 一个接口返回 415,你会检查哪些地方?
- 配置项明明写了却没有生效,可能原因有哪些?
5. 一套面试题
- Spring 的核心思想是什么?
- IoC 和 DI 有什么区别与联系?
- Spring Bean 的生命周期是什么?
- Spring 如何解决横切逻辑问题?
- AOP 和动态代理是什么关系?
- Spring 中事务是如何实现的?
- Spring 事务什么时候会失效?
- Spring MVC 的请求流程是什么?
- Spring Boot 自动配置原理你怎么理解?
- 为什么项目里通常把事务写在 Service 层?
@Autowired和@Resource的区别是什么?- 如何排查一个 Bean 注入失败的问题?
6. 一套实战任务
任务 1:最小容器使用
- 新建一个 Spring Boot 项目。
- 编写
UserService和OrderService。 - 使用构造器注入。
- 启动项目并打印调用结果。
任务 2:参数校验与异常处理
- 编写
POST /users接口。 - 使用
@Valid做参数校验。 - 使用
@RestControllerAdvice返回统一错误信息。
任务 3:事务与排错
- 写一个下单 Service。
- 在其中模拟两步数据库操作。
- 中间抛出异常,观察事务回滚。
- 再做一次"同类内部调用"实验,验证事务失效。
7. 一套项目任务
项目名称
订单中心 Demo
目标
实现一个最小但完整的 Spring Boot 服务,至少包括:
- 用户下单接口。
- 查询订单接口。
- 参数校验。
- 统一异常处理。
- 配置文件按环境拆分。
- Service 层事务。
- 基础日志打印。
- 至少一项常见错误的排查记录。
建议模块
controller:订单接口。service:下单、查单逻辑。repository或mapper:数据访问。dto/vo:入参与返回对象。config:配置绑定、MVC 配置。common:统一返回、异常体系。
进阶要求
- 增加一个支付实现接口,演示多实现 Bean 注入。
- 增加一个 AOP 日志切面。
- 演示一个事务失效案例,并写出原因分析。
8. 对你回答的评分标准
总分 100 分,建议这样评估:
| 维度 | 分值 | 评分标准 |
|---|---|---|
| 概念理解 | 20 | 能否清楚解释 IoC、Bean、AOP、事务、MVC |
| 原理理解 | 20 | 能否讲清代理、生命周期、事务链路 |
| 编码能力 | 25 | 能否独立写出合理结构的 Spring Boot 项目 |
| 排错能力 | 15 | 能否系统定位 Bean、事务、MVC、配置问题 |
| 面试表达 | 20 | 回答是否结构化、能否结合场景和坑点 |
分档说明
- 90-100:中级达标,能讲原理、能做项目、能排错。
- 75-89:初级偏强,开发可用,原理还需巩固。
- 60-74:入门达标,但机制理解不稳。
- 60 以下:需要回到主干重新补基础。
9. 如何根据错误结果反向补课
| 错误表现 | 暴露的问题 | 回补阶段 |
|---|---|---|
| 说不清 Spring 是什么 | 整体认知不足 | 第一阶段 |
| Bean、IoC、DI 混淆 | 核心概念不稳 | 第二阶段 |
| 解释不清事务为何生效 | AOP/代理机制没吃透 | 第三阶段 |
| 听不懂后置处理器 | 底层主流程薄弱 | 第四阶段 |
| 项目结构混乱 | 工程实践不足 | 第五阶段 |
| 一遇报错就乱改注解 | 排错方法没建立 | 第六阶段 |
| 面试回答散乱 | 没形成表达模板 | 第七阶段 |
10. 下一步继续深入该学什么
当你完成这份手册后,建议按下面顺序继续:
- Spring Boot 深入:自动配置源码、Starter 机制。
- 数据访问:MyBatis / JPA、事务传播、锁与一致性。
- Spring Security:认证、授权、过滤器链。
- 缓存与消息:Redis、MQ 与 Spring 集成。
- 微服务:Spring Cloud、配置中心、服务治理。
最后建议
正确学习顺序再强调一次
- 先建立地图。
- 再学 IoC / Bean。
- 再学 AOP / 事务 / MVC。
- 再做工程实践。
- 再练排错。
- 最后整理面试表达。
企业里通常怎么用它
企业里最常见的组合是:
- Spring Boot 启动服务。
- Spring MVC 提供接口。
- Spring Transaction 管事务。
- MyBatis / JPA 做数据访问。
- Redis / MQ / Security 按业务需要扩展。
面试官通常怎么问它
面试官很少只问"定义",更常见的是:
- Spring 为什么能降低耦合?
- 事务底层怎么实现?
- AOP 和代理什么关系?
- 一次请求在 MVC 里怎么走?
- 你项目里 Spring 是怎么落地的?
- 你遇到过什么 Spring 问题,怎么排查?
实际开发最常踩的坑
- 包扫描范围不对导致 Bean 找不到。
- 同类型多个 Bean 冲突。
@Transactional自调用失效。- 异常被吃掉导致不回滚。
- 把全部业务塞进 Controller。
- DTO、Entity、VO 混用。
- 配置环境切换混乱。
一句话收束
Spring 最值得你真正吃透的,不是注解数量,而是"容器、代理、MVC、工程化"这四条主线。