Spring 从零学习路线与核心原理总览
Spring Framework 不是一组必须背诵的注解,而是一套围绕“对象怎样定义、创建、组装、增强、使用和销毁”建立的基础设施。@Autowired、@Transactional、@RequestMapping 只是入口;真正需要理解的是容器、代理和扩展链如何让这些注解产生行为。
本页是 Spring 栏目的课程入口。它不会重复每个专题的全部源码,而是解决三个学习问题:
- 为什么要学这一知识点;
- 它依赖前面什么原理;
- 学完后应当能完成什么 Demo 和排查任务。
学完后能够做什么
- 从
BeanDefinition讲到最终代理对象; - 区分 IoC、DI、BeanFactory、ApplicationContext;
- 选择构造器、Setter、字段、集合和延迟注入;
- 解释 Bean 生命周期和后置处理器的插入位置;
- 解释循环依赖、三级缓存和早期代理的边界;
- 设计并实现 Spring 扩展点;
- 从 JDK/CGLIB 代理讲到 MethodInterceptor 链;
- 正确设计声明式和编程式事务边界;
- 从 Servlet 请求讲到 Spring MVC JSON 响应;
- 沿
refresh、doGetBean、doCreateBean阅读源码; - 定位启动慢、依赖注入失败、代理失效、事务失效和请求异常;
- 说明 JDK 7/8、Spring 5、Boot 2 与 Spring 6/Boot 3 的版本边界。
一、Spring 到底解决什么问题
1.1 没有容器时的对象图
假设订单服务依赖库存、支付、消息和审计:
public class OrderService {
private final InventoryService inventoryService =
new InventoryService(new JdbcInventoryRepository());
private final PaymentService paymentService =
new PaymentService(new BankHttpClient());
private final NotificationService notificationService =
new NotificationService(new RocketMqClient());
}问题不是 new 本身错误,而是对象创建、配置、生命周期和业务流程被绑在一起:
- 测试无法方便替换支付客户端;
- 数据库 URL、超时和连接池配置散落;
- 同一个客户端可能被重复创建;
- 初始化失败和资源关闭没有统一位置;
- 日志、事务、权限需要侵入每个业务方法;
- 改用另一个实现必须修改业务类;
- 很难观察完整对象依赖图。
1.2 IoC 后的职责划分
业务类只声明依赖:
public class OrderService {
private final InventoryService inventoryService;
private final PaymentService paymentService;
public OrderService(InventoryService inventoryService,
PaymentService paymentService) {
this.inventoryService = inventoryService;
this.paymentService = paymentService;
}
}容器负责:
- 读取对象定义;
- 选择实现;
- 创建实例;
- 解析依赖;
- 执行初始化和销毁;
- 应用后置处理器;
- 必要时返回代理;
- 管理 singleton、prototype 等作用域。
IoC 是控制权反转:业务不再掌控整个对象图。DI 是容器实现 IoC 的主要方式:把解析出的依赖传给构造器、字段或方法。
二、Spring 的核心心智模型
flowchart TD
A["配置类、注解、XML或注册器"] --> B["BeanDefinition创建元数据"]
B --> C["BeanFactory保存和处理定义"]
C --> D["选择构造器或工厂方法实例化"]
D --> E["解析并注入依赖"]
E --> F["执行Aware和初始化回调"]
F --> G["BeanPostProcessor检查并增强"]
G --> H["可能生成AOP代理"]
H --> I["最终Bean进入作用域缓存"]
I --> J["业务通过容器中的引用调用"]
J --> K["容器关闭并执行销毁回调"]2.1 BeanDefinition 不是 Bean
BeanDefinition 是“如何创建”的元数据,可能记录:
- Bean class;
- 工厂 Bean 和工厂方法;
- scope;
- lazy;
- primary;
- dependsOn;
- 构造参数;
- PropertyValues;
- init/destroy method;
- qualifier 和角色等信息。
Bean 是依据定义创建的运行对象。定义阶段可以被注册器和 BFPP 修改;对象阶段可以被 BPP 注入和代理。混淆二者就无法理解自动配置、扩展点和源码顺序。
2.2 容器中拿到的不一定是原始对象
业务注入的引用可能是:
- 普通原始 Bean;
- JDK 动态代理;
- CGLIB 类代理;
- Lazy 代理;
- ScopedProxy;
- FactoryBean 创建的产品;
- 多层代理的最外层对象。
因此不能用 bean.getClass() == Implementation.class 作为通用判断,也不能认为对 this 的调用会再次经过外部代理。
三、Spring Framework、Boot、MVC、Cloud 的关系
| 名称 | 解决的主要问题 | 是否等同Spring核心容器 |
|---|---|---|
| Spring Framework | IoC、AOP、事务、Web、数据访问、测试等基础能力 | 是基础主体 |
| Spring MVC | 基于Servlet的Web MVC请求处理 | Framework的Web模块 |
| Spring Boot | 自动配置、Starter、内嵌容器和生产支持 | 建立在Framework上 |
| Spring Security | 认证、授权和安全过滤器链 | 独立生态项目,集成Framework |
| Spring Data | 统一数据访问抽象 | 独立生态项目 |
| Spring Cloud | 分布式系统组件抽象和集成 | 建立在Boot生态上 |
| Spring AI | 模型、Prompt、RAG、Tool等AI应用抽象 | 建立在Boot生态上 |
学习顺序应先 Framework,再 Boot,再进入 Security、Cloud、AI。不了解 Bean、代理和自动配置扩展链,后面的注解只能停留在使用层。
四、学习前置知识
开始 Spring 前至少应理解:
- Java 类、接口、继承和组合;
- 构造器和方法重载;
- 注解与反射;
- 泛型;
- 异常和 cause 链;
- 集合与线程安全基础;
- JDBC 事务基本概念;
- HTTP 请求、响应和 Servlet 基础;
- Maven 依赖与 classpath。
不要求先读 Spring 源码,但如果连接口实现、代理对象、反射调用和线程本地变量都不了解,学习 AOP 与事务会变成背结论。对应前置可从 JavaSE、JavaEE 栏目补齐。
五、完整学习路线
flowchart TD
A["阶段0:Java与Servlet前置"] --> B["阶段1:IoC与BeanDefinition"]
B --> C["阶段2:依赖注入与生命周期"]
C --> D["阶段3:循环依赖、Lazy与作用域"]
D --> E["阶段4:容器扩展点"]
E --> F["阶段5:AOP与代理调用链"]
F --> G["阶段6:事务边界与传播"]
G --> H["阶段7:Spring MVC请求链"]
H --> I["阶段8:refresh与Bean创建源码"]
I --> J["阶段9:Spring Boot自动配置"]
J --> K["商业场景与生产排查"]
K --> L["验收与面试"]路线不是按照注解数量排列,而是按照原理依赖关系排列:代理必须建立在容器创建对象之上,事务必须建立在代理调用之上,Boot 自动配置必须建立在定义注册和条件装配之上。
六、阶段一:IoC、BeanFactory 与 ApplicationContext
为什么先学
后续所有能力都要求对象进入 Spring 容器。手工 new 的对象不会自动获得注入、事务、缓存或安全切面。
必须掌握
- IoC 与 DI 的区别;
- BeanDefinition 怎样注册;
- BeanFactory 与 ApplicationContext;
refresh()主要阶段;getBean()主线;- singleton、prototype 和自定义 scope;
- FactoryBean 与 BeanFactory;
- 父子容器;
@Configuration与@Bean。
最低实践
不用 Spring Boot,使用 AnnotationConfigApplicationContext 创建接口实现、注入 Service、取得 Bean 并关闭容器。
如果跳过
会把 @Autowired 当成 Java 语法,把 Boot 自动配置当成扫描魔法,无法解释为什么手工 new 导致事务失效。
进入课程:Spring IoC容器与依赖注入全过程。
七、阶段二:依赖注入与 Bean 生命周期
必须掌握
- 构造器、Setter、字段和方法注入;
@Autowired候选算法;@Resource名称解析;- Primary、Qualifier、泛型和集合注入;
- Optional、Nullable、ObjectProvider;
- 实例化、属性填充和初始化的区别;
- Aware、PostConstruct、InitializingBean、init-method;
- PreDestroy、DisposableBean、destroy-method;
- BeanPostProcessor 前后置回调。
为什么推荐构造器注入
- 强制依赖在创建时完整;
- 字段可
final; - 测试可直接 new;
- 依赖关系可见;
- 构造器循环依赖会立即暴露设计问题。
如果不会会怎样
- 构造器中读取字段注入值为 null;
- 多个实现时注入错误对象;
- JDK 代理导致具体类型注入失败;
- 初始化方法做重活导致启动慢;
- 关闭时线程池和连接未释放。
进入课程:
八、阶段三:循环依赖、Lazy 与作用域
必须掌握
- 构造器循环为什么无法提前暴露;
- singleton 三级缓存;
- earlyBeanReference 与早期代理;
- prototype 循环边界;
- Boot 默认循环依赖策略;
- 定义级 Lazy 与注入点 Lazy 代理;
- singleton 注入 prototype;
- ObjectProvider 与 ScopedProxy。
关键原则
容器能解决一部分循环依赖,不代表设计合理。优先通过职责拆分、协调服务或事件建立单向依赖。Lazy 代理只改变创建时机,也可能把启动错误推迟到真实流量。
进入课程:
九、阶段四:Spring 扩展点
为什么要学
Spring Boot Starter、自动配置、MyBatis Mapper、事务、AOP、配置绑定和框架集成,都依赖 Spring 扩展链。只会写 Service 和 Controller,无法设计可复用基础设施。
按生命周期掌握
| 阶段 | 扩展点 | 操作对象 |
|---|---|---|
| 注册定义 | BeanDefinitionRegistryPostProcessor | BeanDefinition注册表 |
| 修改定义 | BeanFactoryPostProcessor | BeanDefinition/BeanFactory |
| 实例前后 | InstantiationAwareBeanPostProcessor | Bean创建过程 |
| 初始化前后 | BeanPostProcessor | Bean实例/代理 |
| 特殊产品 | FactoryBean | 复杂产品对象 |
| 配置导入 | ImportSelector/Registrar | 配置和定义 |
| 事件 | ApplicationEventPublisher/Listener | 事件通知 |
| 容器启停 | Lifecycle/SmartLifecycle | 生命周期组件 |
如果选错扩展点
- BFPP 中获取普通 Bean,导致过早创建;
- BPP 保存请求状态,产生线程安全问题;
- FactoryBean 工厂和产品类型混淆;
- 监听器执行长任务拖慢发布线程;
- ImportSelector 做远程 I/O 导致启动不可控。
进入课程:Spring核心扩展点与Starter扩展链全过程。
十、阶段五:AOP 与代理调用链
必须掌握
- Aspect、Advisor、Pointcut、Advice;
- AutoProxyCreator;
- JDK 动态代理和 CGLIB;
- MethodInterceptor 与
proceed(); - 多切面顺序;
- Before、After、Around 的异常和返回语义;
- 自调用为什么绕过代理;
- final/private/static 限制;
- 早期代理与循环依赖;
- AOP 与 AspectJ 的边界。
核心链路
flowchart TD
A["目标Bean完成初始化"] --> B["AutoProxyCreator查找Advisor"]
B --> C{"Pointcut匹配?"}
C -- "否" --> D["返回原始Bean"]
C -- "是" --> E["ProxyFactory创建代理"]
E --> F["业务持有代理引用"]
F --> G["外部方法调用进入代理"]
G --> H["MethodInterceptor链"]
H --> I["调用目标方法"]
I --> J["按调用栈反向退出增强"]如果不会会怎样
- 认为加了注解一定生效;
this.method()导致事务和缓存失效;- Around 忘记
proceed()让业务不执行; - 调用两次
proceed()造成重复扣款; - 吞异常改变事务回滚;
- 按实现类注入 JDK 代理失败。
进入课程:Spring AOP代理与拦截器链全过程。
十一、阶段六:Spring 事务
必须掌握
- 声明式事务与编程式事务;
- TransactionInterceptor;
- PlatformTransactionManager;
- 逻辑事务与物理事务;
- REQUIRED、REQUIRES_NEW、NESTED;
- rollback-only 与 UnexpectedRollbackException;
- checked exception 回滚规则;
- 事务同步和线程绑定资源;
- MyBatis/JPA 如何加入同一事务;
- afterCommit 为什么不能保证消息可靠;
- 连接池、锁和事务超时。
商业边界
数据库事务不能自动覆盖 MQ、Redis、ES 和远程服务。跨资源一致性要结合本地消息表、事务消息、CDC、补偿或分布式事务方案。
如果不会会怎样
- 自调用导致事务不生效;
- catch 异常后错误提交;
- REQUIRES_NEW 耗尽连接池;
- 长事务持锁导致高并发阻塞;
- afterCommit 发送 MQ 仍然丢消息;
- 异步线程丢失事务上下文。
进入课程:
十二、阶段七:Spring MVC
必须掌握
- Servlet 容器和 Spring 容器边界;
- DispatcherServlet 初始化;
- HandlerMapping 与 HandlerAdapter;
- RequestMapping 注册和匹配;
- 参数解析、转换、绑定和校验;
- HttpMessageConverter;
- Content-Type、Accept、415和406;
- ReturnValueHandler;
- HandlerInterceptor;
- HandlerExceptionResolver;
- Callable、DeferredResult 与异步重派发;
- response committed。
如果不会会怎样
面对 400、404、415、406 或 Controller 返回后的 500,不知道问题发生在网关、映射、参数、业务、序列化还是响应提交阶段。
进入课程:Spring MVC请求执行链与扩展点全过程。
十三、阶段八:Spring 容器源码
阅读顺序
AbstractApplicationContext.refresh();invokeBeanFactoryPostProcessors();registerBeanPostProcessors();preInstantiateSingletons();doGetBean();createBean();doCreateBean();populateBean();initializeBean();close()与destroySingletons()。
源码学习的目标不是背行号,而是能用调用栈解释注入、代理、缓存和回滚。
进入课程:Spring容器启动与Bean创建源码执行链。
十四、阶段九:Spring Boot
Framework 掌握后再进入 Boot:
- SpringApplication 启动;
- 环境和配置文件;
- 自动配置候选导入;
@Conditional;- Starter;
- 配置属性绑定;
- 内嵌 Tomcat;
- Actuator;
- 日志、异常和校验;
- 自定义自动配置与 Starter;
- 启动诊断和生产部署。
Boot 没有替代 Spring Framework,它自动组装 Framework 的 BeanDefinition 和基础设施。进入课程:Spring Boot从零到生产级掌握。
十五、一个商业请求怎样串起全部知识
以“创建订单并可靠通知”为例:
flowchart TD
A["HTTP请求进入Filter链"] --> B["DispatcherServlet匹配Controller"]
B --> C["参数转换、绑定和校验"]
C --> D["Controller调用OrderService代理"]
D --> E["TransactionInterceptor开启事务"]
E --> F["OrderService调用Repository"]
F --> G["写订单和本地消息表"]
G --> H["事务提交"]
H --> I["异步任务投递MQ"]
I --> J["消费者幂等处理"]
J --> K["失败重试和补偿"]这里同时用到:
- IoC:Controller、Service、Repository和客户端由容器组装;
- 生命周期:连接池和客户端统一初始化、关闭;
- AOP:事务拦截器包围 Service 方法;
- 事务:订单与本地消息在一个数据库事务;
- MVC:JSON 解析、校验和响应;
- 扩展点:异常处理、参数解析、事件或自定义 Starter;
- Boot:自动配置数据源、MVC、Jackson和监控。
如果只学会注解写法而不懂这些连接关系,线上出现自调用、消息丢失、序列化异常或关闭丢任务时就无法定位。
十六、Spring 模块导航地图
| 想解决的问题 | 首先阅读 | 深入阅读 |
|---|---|---|
| 对象为什么由容器创建 | IoC全过程 | 容器源码 |
| 字段为什么没注入 | Autowired与Resource | 生命周期 |
| 多实现怎样选择 | Autowired候选算法 | IoC依赖解析 |
| Bean何时初始化和销毁 | Bean生命周期 | 源码initializeBean |
| 为什么首次调用才创建 | Lazy全过程 | preInstantiateSingletons |
| 循环依赖为何有时成功 | 循环依赖 | 三级缓存源码 |
| 怎样开发基础组件 | 核心扩展点 | 后置处理器源码 |
| 事务和日志为何依赖代理 | AOP全过程 | AOP接入Bean创建 |
| 事务为何失效 | 事务全过程 | AOP自调用 |
| 请求为何返回400/415/406 | Spring MVC | MVC生产Runbook |
| 启动为什么慢 | 源码启动排查 | Lazy风险 |
| 面试如何组织回答 | Spring面试页 | 对应原理锚点 |
十七、版本选择与常见区别
| 技术线 | Java基线 | 包体系与特点 |
|---|---|---|
| Spring 4.x | 常见JDK 7/8 | 传统存量项目,部分现代API尚未出现 |
| Spring 5.3 | JDK 8+ | 重要长期存量线,本文主要验证基线 |
| Spring Boot 2.7 | JDK 8+ | Boot 2末代主线之一,仍使用javax生态 |
| Spring 6 | Java 17+ | Jakarta EE 9+,Servlet等迁到jakarta包 |
| Spring Boot 3 | Java 17+ | 基于Spring 6和Jakarta,不兼容JDK 8 |
JDK 7/8 学习重点
- 普通 POJO,不使用 record;
- JDK 8 lambda 可用,但教学 Demo 应说明版本;
javax.annotation.Resource、javax.servlet、javax.validation常见;- Spring 5.3 与 Boot 2.7 是常见兼容组合;
- JDK 11 后部分 Java EE API 从 JDK 移除,需要显式依赖;
- Spring 6 不能通过只改 import 在 JDK 8 上运行。
迁移时必须同时评估 JDK、Spring、Boot、Servlet容器、Validation、JPA、Security和第三方 Starter,不能单独升级一个坐标。
十八、第一个 JDK 8 可运行 Spring Demo
这个 Demo 不依赖 Spring Boot,先证明 BeanDefinition、构造器注入、singleton 和销毁。金额使用 BigDecimal,避免商业金额使用 double 或 float。
18.1 Maven依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.39</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>18.2 完整代码
import java.math.BigDecimal;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class SpringFirstDemo {
interface PaymentGateway {
String pay(String orderId, BigDecimal amount);
}
static class MockPaymentGateway implements PaymentGateway,
DisposableBean {
public String pay(String orderId, BigDecimal amount) {
return "PAID:" + orderId + ":" + amount.toPlainString();
}
public void destroy() {
System.out.println("paymentGateway closed");
}
}
static class OrderService {
private final PaymentGateway paymentGateway;
OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
String checkout(String orderId, BigDecimal amount) {
if (amount == null || amount.signum() <= 0) {
throw new IllegalArgumentException("amount必须大于0");
}
return paymentGateway.pay(orderId, amount);
}
}
@Configuration
static class Config {
@Bean
PaymentGateway paymentGateway() {
return new MockPaymentGateway();
}
@Bean
OrderService orderService(PaymentGateway gateway) {
return new OrderService(gateway);
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
try {
OrderService first = context.getBean(OrderService.class);
OrderService second = context.getBean(OrderService.class);
if (first != second) {
throw new AssertionError("默认scope应为singleton");
}
String result = first.checkout(
"ORDER-1001", new BigDecimal("99.90"));
if (!"PAID:ORDER-1001:99.90".equals(result)) {
throw new AssertionError(result);
}
System.out.println(result);
System.out.println("singleton=" + (first == second));
} finally {
context.close();
}
}
}期望输出:
PAID:ORDER-1001:99.90
singleton=true
paymentGateway closed18.3 每一步发生了什么
- 配置类先作为 BeanDefinition 注册;
- ConfigurationClassPostProcessor 解析两个
@Bean方法; paymentGateway和orderService成为工厂方法定义;- 启动预实例化 singleton;
- 创建 OrderService 前先解析 PaymentGateway 参数;
- 调用
paymentGateway()创建网关; - 调用
orderService(gateway)完成构造器注入; - 两次 getBean 命中同一 singleton;
- 业务方法使用 BigDecimal 支付;
- context.close 触发 DisposableBean.destroy。
这个 Demo 没有 AOP、事务和 MVC,目的是先验证容器最小主线。继续学习时逐层增加能力,每增加一层都能解释它在哪里接入。
十九、从最小 Demo 逐步扩展
| 练习 | 修改内容 | 证明什么 |
|---|---|---|
| 练习1 | 增加第二个PaymentGateway | 多候选与Qualifier |
| 练习2 | 增加PostConstruct和destroy | 生命周期顺序 |
| 练习3 | 把Gateway改prototype | scope差异 |
| 练习4 | 注入ObjectProvider | 每次获取prototype |
| 练习5 | 增加自定义BeanPostProcessor | 实例扩展点 |
| 练习6 | 增加Aspect | 代理和拦截器链 |
| 练习7 | 增加H2和TransactionTemplate | 编程式事务 |
| 练习8 | 增加Transactional | 声明式事务与自调用 |
| 练习9 | 增加Spring MVC | HTTP到Service调用链 |
| 练习10 | 改为Spring Boot | 自动配置与内嵌容器 |
二十、商业项目设计原则
- Controller 只接收、校验和转换响应,业务放 Service;
- 写操作明确事务边界,不把远程长调用无脑放进事务;
- 强制依赖优先构造器注入;
- singleton Service 不保存当前请求可变状态;
- BFPP/BPP 不执行无超时远程 I/O;
- Bean 初始化和销毁必须有超时、幂等和异常处理;
- 面向接口,但接口应表达真实替换边界,不为每个类机械建接口;
- 自定义扩展点要明确顺序、线程安全和作用范围;
- AOP 切面不能吞异常或意外修改业务返回;
- 日志不输出密码、token、密钥、身份证和医疗敏感数据;
- 金额使用 BigDecimal;
- 时间明确时区;
- 核心能力启动失败应阻止实例接流量;
- 非核心 Lazy 能力要有预热、首调指标和降级。
二十一、常见误区与后果
| 误区 | 后果 | 正确方向 |
|---|---|---|
| Spring就是Autowired | 不懂定义、生命周期和代理 | 从BeanDefinition开始 |
| 加注解一定生效 | 手工new、自调用、条件不匹配时失效 | 确认对象来源和调用入口 |
| 接口越多越解耦 | 无意义抽象增加维护成本 | 围绕替换和业务边界抽象 |
| 字段注入最方便所以最好 | 隐藏依赖、难测试、构造时不完整 | 强制依赖优先构造器 |
| 能解决循环依赖就不用重构 | 双向耦合长期存在 | 建立单向依赖 |
| AOP等于AspectJ | 混淆代理与字节码织入 | 先掌握Spring代理AOP |
| Transactional覆盖所有资源 | MQ、Redis、ES仍可能不一致 | 设计跨资源一致性 |
| 全局Lazy能优化启动 | 错误和耗时转移到首个请求 | 找启动慢根因并预热 |
| Controller返回就代表成功 | 序列化和网络写出仍可能失败 | 覆盖返回值处理与监控 |
| 会背源码方法名就算掌握 | 遇到线上问题仍不会定位 | 用断点和Demo验证状态变化 |
二十二、生产问题应该从哪里进入
| 现象 | 第一检查页 |
|---|---|
| NoSuch/NoUnique/UnsatisfiedDependency | 依赖注入异常诊断 |
| 启动很慢或卡死 | 容器启动慢Runbook |
| Bean没有代理 | 代理缺失排查 |
| Transactional不生效 | Spring事务 |
| 首次请求突然很慢 | Lazy生产排查 |
| BeanCurrentlyInCreation | 循环依赖 |
| HTTP 400/404/415/406/500 | MVC错误阶段表 |
| 关闭卡住或资源未释放 | 容器关闭源码 |
二十三、怎样判断自己真正掌握
不能只回答“Spring 核心是 IoC 和 AOP”。至少应能够:
- 手写完整可运行容器 Demo;
- 画出 BeanDefinition 到最终代理的流程;
- 解释构造器和字段注入的不同阶段;
- 分析多个候选的选择结果;
- 解释 FactoryBean 的
&; - 解释定义级和注入点 Lazy;
- 画三级缓存和早期代理;
- 编写一个安全的 BPP 或 ImportSelector;
- 解释 MethodInterceptor 洋葱调用;
- 复现自调用事务失效;
- 区分 REQUIRED、REQUIRES_NEW 和 NESTED;
- 从 Content-Type 与 Accept 区分 415、406;
- 用断点追踪 refresh 和 doCreateBean;
- 用线程栈和 JFR 定位启动卡死;
- 说明 JDK 8 与 Spring 6 的不兼容边界。
完整验收请进入:Spring从零到精通验收清单。
二十四、面试回答组织方法
回答原理题按以下顺序:
一句话定义
↓
为什么需要
↓
核心参与者
↓
完整执行流程
↓
版本和适用边界
↓
不这样会怎样
↓
商业项目使用
↓
生产排查方法例如回答 @Transactional,不能只说“基于 AOP”:还要说明代理创建、TransactionInterceptor、事务管理器、线程绑定资源、传播行为、提交回滚、自调用、异常规则、连接池风险和跨资源一致性边界。
面试标准回答集中在:Spring面试知识点。详细原理保留在各知识页,面试页通过锚点跳回来。
二十五、推荐学习顺序清单
- 当前总览页;
- 从零到生产级掌握;
- IoC容器与依赖注入;
- Bean生命周期;
@Autowired与@Resource;- Lazy;
- 循环依赖;
- 核心扩展点;
- AOP代理与拦截器链;
- Spring事务;
- Spring MVC;
- 容器源码执行链;
- Spring Boot;
- 从零到精通验收清单;
- Spring面试知识点。
学习时每完成一页,都应运行 Demo、故意制造至少一个错误、根据异常和日志定位,再进入下一页。只看不运行,很难形成对容器状态和调用链的真实理解。
