Spring Framework 全面专题详解|从 0 基础到源码、生产实战与 Java 后端面试
定位:这不是一份"Spring 八股速记",而是一份面向 Java 后端开发者的系统学习资料。目标不是让你记住几个术语,而是让你真正理解 Spring 为什么这样设计、对象怎样进入容器、注解什么时候生效、代理对象什么时候产生、事务为什么会失效,以及生产环境出现问题时应该从哪里排查。
统一讲解模板:
是什么 → 为什么需要 → 专业术语解释 → 底层原理 → 核心源码类/方法 → 简化源码解读 → 完整 Java/配置示例 → 真实业务场景 → 适合场景 → 不适合场景/局限 → 常见坑 → 生产注意事项 → 故障排查 → 面试复盘
版本说明(2026-10):
- Spring Framework 官方稳定版本:7.0.9
- 同时维护的 6.x 稳定线:6.2.19
- Spring Framework 6.0+ 要求 Java 17+
- Spring 6+/7 使用现代 Jakarta (
jakarta.*) 命名空间- 本文核心原理对 Spring 5/6/7 大量场景都成立;涉及内部源码类或 API 差异时以实际项目版本为准
阅读原则:不要一开始硬背源码行号。先把"为什么"和"调用链"讲清楚,再去看源码具体实现,会容易很多。
总目录
- [一、Spring Framework 到底是什么](#一、Spring Framework 到底是什么)
- [二、Spring 核心模块与整个知识体系](#二、Spring 核心模块与整个知识体系)
- [三、IoC、DI 与"控制反转"到底反转了什么](#三、IoC、DI 与“控制反转”到底反转了什么)
- [四、BeanDefinition:Bean 的"说明书"](#四、BeanDefinition:Bean 的“说明书”)
- [五、Bean 注册:一个 @Service 是怎样进入容器的](#五、Bean 注册:一个 @Service 是怎样进入容器的)
- [六、BeanFactory 与 ApplicationContext](#六、BeanFactory 与 ApplicationContext)
- [七、ApplicationContext.refresh() 容器启动源码主线](#七、ApplicationContext.refresh() 容器启动源码主线)
- [八、Bean 实例化:Spring 怎样真正创建对象](#八、Bean 实例化:Spring 怎样真正创建对象)
- [九、依赖注入:@Autowired 到底怎么找到对象](#九、依赖注入:@Autowired 到底怎么找到对象)
- [十、Bean 生命周期完整流程](#十、Bean 生命周期完整流程)
- 十一、Aware、初始化与销毁回调怎么选
- 十二、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor、BeanPostProcessor
- 十三、FactoryBean:为什么接口没有实现类也能注入代理对象
- [十四、Bean Scope 与单例 Bean 线程安全](#十四、Bean Scope 与单例 Bean 线程安全)
- 十五、循环依赖与三级缓存
- [十六、AOP 核心概念](#十六、AOP 核心概念)
- [十七、Spring AOP 代理创建源码主线](#十七、Spring AOP 代理创建源码主线)
- [十八、JDK 动态代理与 CGLIB](#十八、JDK 动态代理与 CGLIB)
- [十九、Advice、Advisor、Pointcut 与拦截器链](#十九、Advice、Advisor、Pointcut 与拦截器链)
- [二十、AOP 自调用失效与生产坑](#二十、AOP 自调用失效与生产坑)
- [二十一、Spring 声明式事务完整原理](#二十一、Spring 声明式事务完整原理)
- [二十二、TransactionManager、TransactionInterceptor 与线程资源绑定](#二十二、TransactionManager、TransactionInterceptor 与线程资源绑定)
- 二十三、事务传播行为
- 二十四、事务隔离级别、readOnly、timeout、rollbackFor
- [二十五、@Transactional 失效场景](#二十五、@Transactional 失效场景)
- [二十六、Spring MVC 整体架构与请求链路](#二十六、Spring MVC 整体架构与请求链路)
- 二十七、DispatcherServlet、HandlerMapping、HandlerAdapter
- [二十八、参数解析、数据绑定与 HttpMessageConverter](#二十八、参数解析、数据绑定与 HttpMessageConverter)
- [二十九、Validation 参数校验](#二十九、Validation 参数校验)
- 三十、全局异常处理
- [三十一、Filter、Interceptor、AOP 如何选](#三十一、Filter、Interceptor、AOP 如何选)
- [三十二、Spring Event 事件机制](#三十二、Spring Event 事件机制)
- [三十三、Resource 资源抽象](#三十三、Resource 资源抽象)
- 三十四、ConversionService、Formatter、DataBinder
- [三十五、SpEL 表达式语言](#三十五、SpEL 表达式语言)
- [三十六、JdbcTemplate 与 Spring JDBC](#三十六、JdbcTemplate 与 Spring JDBC)
- 三十七、数据访问异常统一转换
- [三十八、REST Client、WebClient 与 HTTP 调用](#三十八、REST Client、WebClient 与 HTTP 调用)
- 三十九、异步任务、定时任务与线程池
- [四十、Spring Cache](#四十、Spring Cache)
- [四十一、Spring Test 测试体系](#四十一、Spring Test 测试体系)
- [四十二、AOT 与现代 Spring](#四十二、AOT 与现代 Spring)
- [四十三、Spring 中常见设计模式](#四十三、Spring 中常见设计模式)
- 四十四、生产环境性能问题与故障排查
- [四十五、Spring 源码阅读路线](#四十五、Spring 源码阅读路线)
- 四十六、高频面试题与连续追问
- [四十七、一张主线串起整个 Spring](#四十七、一张主线串起整个 Spring)
一、Spring Framework 到底是什么
1.1 是什么
Spring Framework 是 Java 企业级开发领域最核心的基础框架之一。
如果只用一句话概括:
text
Spring 是一个"对象管理容器 + 基础设施框架"。
"对象管理容器"主要指:
text
IoC / DI
Bean 生命周期
Bean 依赖关系
Bean 扩展机制
"基础设施框架"则包括:
text
AOP
事务
MVC
数据访问
事件
资源管理
类型转换
校验
表达式语言
异步
定时
缓存
测试
HTTP 客户端
AOT
1.2 为什么 Java 项目需要 Spring
假设不用 Spring:
java
public class OrderService {
private final UserService userService =
new UserService();
private final PaymentService paymentService =
new PaymentService();
public void createOrder() {
// ...
}
}
问题看起来不大,但系统一复杂就会出现:
text
对象创建散落在各个类
类和具体实现强耦合
对象很难统一替换
测试时难注入 Mock
事务代码到处重复
日志/权限/审计到处复制
生命周期无法统一管理
Spring 把思路改为:
text
对象不要由业务类自己 new
↓
对象交给容器创建
↓
依赖关系交给容器装配
↓
公共基础能力通过代理和扩展点织入
业务类变成:
java
@Service
public class OrderService {
private final UserService userService;
private final PaymentService paymentService;
public OrderService(
UserService userService,
PaymentService paymentService) {
this.userService = userService;
this.paymentService = paymentService;
}
}
此时:
text
OrderService 只关心:
"我要 UserService 和 PaymentService"
而不关心:
"它们怎么创建、生命周期多久、有没有代理"
1.3 Spring 最重要的两条主线
IoC
解决:
text
对象由谁创建
对象如何管理
对象之间如何建立依赖
AOP
解决:
text
如何在不污染业务代码的情况下
统一增加事务、日志、权限、监控等横切能力
Spring 的声明式事务,本质上就是 AOP 的典型应用。
1.4 Spring 不等于 Spring Boot
一定区分:
text
Spring Framework
= 核心能力
Spring Boot
= 基于 Spring 的自动配置、工程化、运行和生产能力
例如:
text
BeanFactory
AOP
@Transactional
DispatcherServlet
属于 Spring Framework 核心体系。
而:
text
@SpringBootApplication
AutoConfiguration
Starter
Actuator
主要属于 Spring Boot。
1.5 适合场景
Spring 非常适合:
- 企业后台系统
- REST API
- 微服务底层应用
- 工作流系统
- 审批系统
- 电商/支付/订单
- 数据平台
- MQ 消费应用
- 集成平台
1.6 不适合/局限
Spring 的代价包括:
text
抽象层多
Bean 生命周期复杂
代理调用链较深
内部扩展点多
不理解原理时问题不好定位
一个只有几十行代码的简单 CLI 工具,未必需要完整 Spring 容器。
1.7 面试复盘
Q:Spring 最核心的价值是什么?
推荐回答:
Spring 最核心的能力是 IoC 和 AOP。IoC 把对象创建、生命周期和依赖关系交给容器管理,降低业务类之间的直接耦合;AOP 通过代理和拦截器链实现事务、日志、权限等横切能力,让业务代码关注业务本身。在此基础上,Spring 又提供事务、MVC、数据访问、事件、校验等完整企业级基础设施。
[⬆ 返回总目录](#⬆ 返回总目录)
二、Spring 核心模块与整个知识体系
Spring Framework 不只是:
text
IoC + AOP + MVC
当前完整体系大致可以按以下维度理解。
2.1 Core Container
包括:
text
spring-core
spring-beans
spring-context
spring-expression
负责:
text
IoC
BeanFactory
ApplicationContext
资源
事件
类型转换
SpEL
2.2 AOP
主要:
text
spring-aop
解决:
text
代理
Pointcut
Advice
Advisor
Interceptor Chain
2.3 Data Access
包括:
text
事务
Spring JDBC
ORM 集成
R2DBC
2.4 Web
包括:
text
Spring MVC
WebFlux
WebSocket
REST Client
WebClient
2.5 Integration
包括:
text
Task
Scheduling
Cache
JMS
邮件
Observability
2.6 Testing
包括:
text
Spring TestContext
MockMvc
WebTestClient
事务测试
Context 缓存
2.7 最重要的学习顺序
推荐:
text
IoC/Bean
↓
Bean 生命周期
↓
扩展点
↓
循环依赖
↓
AOP/代理
↓
事务
↓
Spring MVC
↓
数据访问
↓
其他集成能力
为什么:
text
事务依赖 AOP
AOP 依赖 Bean 生命周期
Bean 生命周期依赖 IoC
MVC Controller 本质也是 Bean
所以必须先学"容器"。
[⬆ 返回总目录](#⬆ 返回总目录)
三、IoC、DI 与"控制反转"到底反转了什么
3.1 IoC 是什么
IoC:
text
Inversion of Control
控制反转
这里的"控制"主要指:
text
对象创建权
依赖选择权
生命周期管理权
传统:
java
PaymentService payment =
new AliPayService();
业务代码决定:
text
创建谁
什么时候创建
创建几份
Spring:
java
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(
PaymentService paymentService) {
this.paymentService = paymentService;
}
}
业务类只声明:
text
我需要 PaymentService
到底注入:
text
AliPayService
WechatPayService
MockPaymentService
由容器装配。
3.2 为什么叫"反转"
以前:
text
对象主动寻找依赖
现在:
text
容器主动提供依赖
因此控制方向反转。
3.3 DI 是什么
DI:
text
Dependency Injection
依赖注入
它是 IoC 思想最典型的实现方式。
IoC 是理念:
text
把控制权交给容器
DI 是动作:
text
容器把依赖注入对象
3.4 三种常见注入
构造器注入
java
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(
PaymentService paymentService) {
this.paymentService = paymentService;
}
}
Setter 注入
java
@Autowired
public void setPaymentService(
PaymentService paymentService) {
this.paymentService = paymentService;
}
字段注入
java
@Autowired
private PaymentService paymentService;
3.5 为什么推荐构造器注入
因为依赖:
text
显式
不可变
容易测试
容易发现循环依赖
例如:
java
OrderService service =
new OrderService(mockPaymentService);
普通单元测试不需要反射去设置字段。
3.6 构造器参数太多说明什么
如果:
java
public OrderService(
A a, B b, C c, D d, E e,
F f, G g, H h, I i, J j) {
}
不要第一反应:
text
"Spring 注入真麻烦"
更可能是:
text
这个类职责太多
需要拆分
构造器注入会把设计问题暴露出来。
3.7 字段注入为什么不推荐
字段注入方便:
java
@Autowired
private UserService userService;
但问题:
text
依赖隐藏
字段可变
单元测试不方便
容易形成巨型 Service
脱离容器不好实例化
并不是说:
text
字段注入一定不能工作
而是工程可维护性较差。
3.8 实际业务场景
支付系统:
java
public interface PaymentGateway {
void pay(Order order);
}
实现:
java
@Component
public class AliPaymentGateway
implements PaymentGateway {
}
业务:
java
@Service
public class PaymentApplicationService {
private final PaymentGateway gateway;
public PaymentApplicationService(
PaymentGateway gateway) {
this.gateway = gateway;
}
}
这样 Service 不关心:
text
Gateway 具体如何创建
3.9 适合场景
IoC/DI 几乎适合所有:
text
业务 Service
Repository
Controller
Client
策略实现
基础设施组件
3.10 不适合什么
不要把所有普通 DTO、VO、Entity 都注册成 Bean。
例如:
java
@Component
public class CreateOrderRequest {
}
通常没有意义。
请求 DTO 应该:
text
按请求创建
不是容器级业务组件
[⬆ 返回总目录](#⬆ 返回总目录)
四、BeanDefinition:Bean 的"说明书"
4.1 BeanDefinition 是什么
很多人以为 Spring 扫到:
java
@Service
public class UserService {}
之后马上:
java
new UserService();
实际上核心思路是:
text
先把"怎么创建这个 Bean"记录下来
这个元数据对象就是:
text
BeanDefinition
可以理解成:
text
BeanDefinition = Bean 的施工图
Bean = 根据施工图创建出来的实例
4.2 BeanDefinition 记录什么
典型包括:
text
Bean class
Scope
Lazy
Primary
DependsOn
构造器信息
属性值
Factory Method
Init Method
Destroy Method
Role
Autowire Candidate
4.3 为什么不能扫描后立即创建 Bean
因为 Spring 需要给扩展机制机会:
text
扫描
↓
BeanDefinition 注册
↓
BeanDefinition 后置处理
↓
修改元数据
↓
再创建 Bean
例如:
text
@Configuration 解析
@Mapper 动态注册
框架 Starter 动态 BeanDefinition
都需要在实例化之前操作元数据。
4.4 常见实现
例如:
text
RootBeanDefinition
GenericBeanDefinition
AnnotatedGenericBeanDefinition
ScannedGenericBeanDefinition
学习时不要求一开始死背区别。
重点:
text
都在描述"Bean 如何被创建"
4.5 BeanDefinitionRegistry
负责:
text
注册 BeanDefinition
典型核心容器:
text
DefaultListableBeanFactory
内部可以粗略理解为:
java
Map<String, BeanDefinition>
真实实现还有顺序、别名、类型缓存等更多结构。
4.6 手动注册示例
java
DefaultListableBeanFactory factory =
new DefaultListableBeanFactory();
GenericBeanDefinition bd =
new GenericBeanDefinition();
bd.setBeanClass(UserService.class);
bd.setScope("singleton");
factory.registerBeanDefinition(
"userService",
bd
);
UserService userService =
factory.getBean(UserService.class);
这就是最基础的:
text
定义
→ 注册
→ 获取
4.7 实际框架应用
MyBatis:
text
Mapper 接口
并没有实现类,但框架可以:
text
扫描 Mapper
↓
动态注册特殊 BeanDefinition / FactoryBean
↓
最终得到代理对象
Feign、RPC、SDK Starter 都有类似思想。
4.8 常见异常
NoSuchBeanDefinitionException
text
需要的 BeanDefinition/Bean 不存在
NoUniqueBeanDefinitionException
text
同类型候选太多
无法唯一确定
BeanDefinitionOverrideException
text
重复定义发生冲突
4.9 面试复盘
Q:BeanDefinition 和 Bean 有什么区别?
BeanDefinition 是 Bean 的元数据描述,记录类型、Scope、初始化方式、构造信息等;真正的 Bean 实例会在后续创建阶段根据 BeanDefinition 实例化和初始化。
[⬆ 返回总目录](#⬆ 返回总目录)
五、Bean 注册:一个 @Service 是怎样进入容器的
5.1 先看表面
java
@Service
public class OrderService {
}
为什么 Spring 能知道它存在?
因为通常配置了:
java
@ComponentScan
或者 Spring Boot 主类间接启用了组件扫描。
5.2 扫描流程
简化:
text
@ComponentScan
↓
ClassPathBeanDefinitionScanner
↓
扫描 classpath
↓
读取类元数据
↓
判断是否是候选组件
↓
生成 BeanDefinition
↓
BeanDefinitionRegistry
5.3 为什么扫描时不一定要创建 Class 实例
Spring 可以读取:
text
.class 元数据
分析:
text
注解
类名
接口
修饰符
不用先:
java
new
对象。
5.4 @Component、@Service、@Repository、@Controller
它们都可参与组件扫描。
区别主要在:
text
语义
某些额外框架行为
例如:
java
@Repository
不仅表达"数据访问层",还与 Spring 的数据访问异常转换体系有关。
5.5 @Bean 注册
第三方对象不能改源码:
java
public class ThirdPartyClient {
}
不能给它加:
java
@Component
可以:
java
@Configuration
public class ClientConfig {
@Bean
public ThirdPartyClient client() {
return new ThirdPartyClient();
}
}
5.6 @Import
java
@Import(MyConfiguration.class)
可以导入:
text
配置类
ImportSelector
ImportBeanDefinitionRegistrar
它是很多框架扩展能力的重要入口。
5.7 ImportSelector
适合:
text
根据条件动态返回一批需要导入的配置类名
Spring Boot 自动配置就大量利用类似机制。
5.8 ImportBeanDefinitionRegistrar
更底层:
text
直接拿 BeanDefinitionRegistry
动态注册 BeanDefinition
适合:
text
RPC Client
Mapper
动态接口代理
框架基础设施
5.9 不要滥用动态注册
普通业务代码如果只是:
text
创建一个 Service
直接:
java
@Service
最清楚。
不要为了"高级"写:
text
ImportBeanDefinitionRegistrar
否则可读性极差。
[⬆ 返回总目录](#⬆ 返回总目录)
六、BeanFactory 与 ApplicationContext
6.1 BeanFactory 是什么
BeanFactory 是 Spring IoC 最基础的容器接口。
核心能力:
text
getBean
containsBean
getType
isSingleton
它回答:
text
"这个 Bean 有没有?"
"给我这个 Bean"
6.2 ApplicationContext 是什么
ApplicationContext 是更完整的应用级容器。
除了 Bean 管理,还提供:
text
事件
资源
国际化
环境
自动发现和注册各种处理器
Web 集成
6.3 关系不要简单说成"继承"
ApplicationContext 接口体系继承 BeanFactory 相关能力,同时具体实现内部也组合/使用底层 BeanFactory。
理解上可以记:
text
BeanFactory = IoC 核心发动机
ApplicationContext = 完整应用容器
6.4 常见实现
text
AnnotationConfigApplicationContext
ClassPathXmlApplicationContext
GenericApplicationContext
Web 环境还有对应 WebApplicationContext。
6.5 "BeanFactory 懒加载,ApplicationContext 饿加载"准确吗
不够准确。
真正是否提前创建取决于:
text
Bean Scope
lazy-init
容器启动过程
preInstantiateSingletons
典型 ApplicationContext 在 refresh 末期会创建非懒加载单例 Bean,但这不意味着所有 Bean 都一定立即实例化。
6.6 业务场景
普通业务项目:
text
几乎都使用 ApplicationContext
BeanFactory 更多出现在:
text
Spring 源码
框架底层
高级扩展
[⬆ 返回总目录](#⬆ 返回总目录)
七、ApplicationContext.refresh() 容器启动源码主线
这是 Spring 源码最重要的方法之一。
如果你把 refresh() 理顺,很多八股题会自动串起来。
7.1 为什么叫 refresh
它并不只是:
text
刷新页面
它代表:
text
准备或刷新一个完整 ApplicationContext
包括:
text
BeanFactory
后置处理器
监听器
单例 Bean
事件系统
7.2 典型入口
java
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
AppConfig.class
);
最终会走到:
text
AbstractApplicationContext.refresh()
7.3 关键主线
简化:
text
refresh()
│
├─ prepareRefresh()
├─ obtainFreshBeanFactory()
├─ prepareBeanFactory()
├─ postProcessBeanFactory()
├─ invokeBeanFactoryPostProcessors()
├─ registerBeanPostProcessors()
├─ initMessageSource()
├─ initApplicationEventMulticaster()
├─ onRefresh()
├─ registerListeners()
├─ finishBeanFactoryInitialization()
└─ finishRefresh()
不同版本具体细节会演进,但主线非常稳定。
7.4 prepareRefresh()
主要做:
text
容器状态初始化
Environment 校验
准备 early events
可以理解:
text
"开始启动了,先把上下文状态准备好"
7.5 obtainFreshBeanFactory()
获取/准备:
text
真正负责 Bean 管理的 BeanFactory
对某些 Context:
text
可能创建新的 BeanFactory
7.6 prepareBeanFactory()
给 BeanFactory 注册:
text
ClassLoader
BeanExpressionResolver
PropertyEditor
ApplicationContextAwareProcessor
一些特殊依赖
环境 Bean
这一步把 BeanFactory 和完整 ApplicationContext 能力连接起来。
7.7 invokeBeanFactoryPostProcessors()
非常关键。
此时:
text
大部分普通 Bean 还没创建
但 BeanDefinition 已经有很多了。
所以它允许:
text
修改 BeanDefinition
继续注册 BeanDefinition
例如:
text
@Configuration 类解析
@ComponentScan
@Bean 方法解析
都与这一阶段的重要处理器有关。
7.8 ConfigurationClassPostProcessor
这是注解配置体系极其重要的处理器。
它会解析:
text
@Configuration
@ComponentScan
@Import
@ImportResource
@Bean
所以:
java
@Configuration
@ComponentScan("com.demo")
public class AppConfig {
}
不是启动前就全部展开完毕。
容器启动时会进一步处理。
7.9 registerBeanPostProcessors()
这一阶段把:
text
BeanPostProcessor
注册进 BeanFactory。
为什么必须先注册?
因为后面 Bean 创建时:
text
@Autowired
@PostConstruct
AOP
等很多能力都需要各种 BeanPostProcessor 参与。
7.10 initApplicationEventMulticaster()
准备:
text
Spring 事件广播器
后面 publishEvent() 就需要它。
7.11 registerListeners()
把监听器注册到事件系统。
7.12 finishBeanFactoryInitialization()
非常重要。
典型工作:
text
准备 ConversionService
嵌入值解析
初始化 LoadTimeWeaver 相关
冻结配置
preInstantiateSingletons
其中:
text
preInstantiateSingletons()
会触发大部分非懒加载单例 Bean 的创建。
7.13 finishRefresh()
完成:
text
生命周期处理
发布 ContextRefreshedEvent 等
说明:
text
容器基本可用了
7.14 为什么理解 refresh 对面试有用
因为可以回答:
text
BeanFactoryPostProcessor 什么时候执行?
BeanPostProcessor 什么时候注册?
单例 Bean 什么时候创建?
事件机制什么时候准备?
@ComponentScan 在哪里被进一步解析?
7.15 生产注意事项
如果应用启动很慢,refresh() 期间重点排查:
text
Bean 构造器
BeanPostProcessor
@PostConstruct
外部连接
Configuration 解析
大范围扫描
非懒加载单例
[⬆ 返回总目录](#⬆ 返回总目录)
八、Bean 实例化:Spring 怎样真正创建对象
8.1 getBean 并不是简单 Map.get
调用:
java
context.getBean("orderService")
可能:
text
单例已经存在 → 直接返回
也可能:
text
第一次获取 → 触发完整创建流程
8.2 核心调用链
学习主线:
text
getBean()
↓
doGetBean()
↓
getSingleton()
↓
createBean()
↓
doCreateBean()
↓
createBeanInstance()
↓
populateBean()
↓
initializeBean()
↓
addSingleton()
内部细节更多,但先掌握这条。
8.3 doGetBean()
大致负责:
text
先查缓存
处理别名
处理 parent BeanFactory
检查 depends-on
获取 BeanDefinition
根据 scope 创建
8.4 createBean()
创建前还可能:
text
让 InstantiationAwareBeanPostProcessor 提前返回代理对象
等高级扩展。
8.5 doCreateBean()
这是 Bean 创建核心。
可以粗略分成三阶段:
text
实例化
↓
属性填充
↓
初始化
同时循环依赖的:
text
early exposure
也主要发生在这里。
8.6 createBeanInstance()
决定怎么实例化:
text
构造器
Factory Method
Supplier
不是所有 Bean 都直接:
java
clazz.getDeclaredConstructor().newInstance()
8.7 populateBean()
负责:
text
依赖注入
属性赋值
@Autowired 相关处理会在这个阶段参与。
8.8 initializeBean()
负责:
text
Aware
初始化前 BPP
初始化方法
初始化后 BPP
AOP 代理通常与:
text
初始化后处理
密切相关。
8.9 为什么创建一个 Bean 会"顺便"创建很多 Bean
例如:
text
OrderService
→ UserService
→ UserRepository
→ DataSource
构造/字段注入时:
text
容器发现依赖没创建
→ 递归 getBean()
因此一个 Bean 的创建形成:
text
依赖图递归解析
[⬆ 返回总目录](#⬆ 返回总目录)
九、依赖注入:@Autowired 到底怎么找到对象
9.1 @Autowired 谁处理
核心后置处理器:
text
AutowiredAnnotationBeanPostProcessor
它本身是一个非常重要的 BeanPostProcessor 扩展。
9.2 什么时候扫描 @Autowired
Bean 创建过程中,会寻找:
text
构造器
字段
方法
上的注入元数据。
然后在属性填充阶段:
text
解析依赖
9.3 依赖解析核心思路
假设:
java
@Autowired
private PaymentService paymentService;
容器需要回答:
text
1. PaymentService 类型有哪些 Bean?
2. 有几个候选?
3. 哪些是 autowireCandidate?
4. 有没有 @Primary?
5. 有没有 @Qualifier?
6. 名称是否匹配?
7. 最终选谁?
9.4 核心源码角色
理解这些名字:
text
DependencyDescriptor
DefaultListableBeanFactory
doResolveDependency()
findAutowireCandidates()
determineAutowireCandidate()
9.5 多实现示例
java
public interface PayService {
void pay();
}
java
@Service
public class AliPayService
implements PayService {
}
java
@Service
public class WechatPayService
implements PayService {
}
注入:
java
@Autowired
private PayService payService;
此时候选:
text
aliPayService
wechatPayService
Spring 无法唯一选择,通常报:
text
NoUniqueBeanDefinitionException
9.6 @Primary
java
@Primary
@Service
public class AliPayService
implements PayService {
}
表示:
text
同类型候选多个时优先考虑我
9.7 @Qualifier
java
public OrderService(
@Qualifier("wechatPayService")
PayService payService) {
}
显式缩小候选范围。
9.8 List 注入
Spring 支持:
java
public PayManager(
List<PayService> payServices) {
}
把同类型多个实现全部注入。
这是策略模式非常实用的方式。
9.9 Map 注入
例如:
java
public PayManager(
Map<String, PayService> payServiceMap) {
}
Key 通常是 Bean name。
可以用于:
text
动态策略路由
9.10 ObjectProvider
如果依赖:
text
可选
延迟获取
可能多实例
可以考虑:
java
ObjectProvider<MyService>
比直接:
text
ApplicationContext.getBean()
更符合依赖注入风格。
9.11 适合策略模式的业务场景
支付渠道:
text
ALI
WECHAT
UNION
可以:
java
Map<String, PayStrategy>
统一注册策略。
不要写巨大的:
java
if ("ALI") ...
else if ("WECHAT") ...
9.12 常见坑
类型太宽
java
@Autowired
private Object service;
没有明确意义。
大量字符串 Qualifier
如果到处:
text
"aliPayService"
硬编码,后期重命名风险高。
可选依赖写成必选
某个插件不存在时:
text
整个应用启动失败
应根据设计使用 Optional/ObjectProvider/条件配置。
[⬆ 返回总目录](#⬆ 返回总目录)
十、Bean 生命周期完整流程
Bean 生命周期是 Spring 面试中最核心、也最容易被"背顺序"带偏的知识点之一。
真正应该理解的是:
text
Spring 为什么需要这么多生命周期阶段?
答案是:
text
因为框架需要在"对象从无到有"的过程中
不断插入扩展逻辑
例如:
text
依赖注入
Aware 回调
@PostConstruct
AOP 代理
事务代理
销毁资源
10.1 宏观流程
先记住主线:
text
BeanDefinition
↓
实例化
↓
提前暴露(特定循环依赖场景)
↓
属性填充 / 依赖注入
↓
Aware 回调
↓
BeanPostProcessor Before
↓
@PostConstruct
↓
InitializingBean
↓
自定义 init-method
↓
BeanPostProcessor After
↓
可能得到代理对象
↓
Bean 可使用
↓
@PreDestroy
↓
DisposableBean
↓
自定义 destroy-method
10.2 第一步:实例化
实例化:
text
只是把 Java 对象创建出来
类似:
java
new UserService()
但此时:
text
@Autowired 字段可能还没有值
所以:
text
实例化 ≠ 初始化完成
10.3 第二步:属性填充
populateBean() 阶段会进行:
text
依赖注入
属性赋值
例如:
java
@Autowired
private UserRepository repository;
会在这里被解析和注入。
10.4 第三步:Aware 回调
Spring 提供一些 Aware 接口,让 Bean 可以感知容器基础设施。
例如:
text
BeanNameAware
BeanFactoryAware
ApplicationContextAware
EnvironmentAware
ResourceLoaderAware
例子:
java
@Component
public class DemoBean
implements BeanNameAware {
@Override
public void setBeanName(
String name) {
System.out.println(
"beanName = " + name
);
}
}
10.5 为什么不推荐业务代码大量实现 Aware
因为会增加:
text
业务对象对 Spring 容器 API 的耦合
普通业务依赖:
text
直接构造器注入
通常更清晰。
Aware 更适合:
text
框架
基础组件
容器扩展
10.6 BeanPostProcessor Before
所有已注册的 BeanPostProcessor 可以在初始化前参与。
简化:
java
for (BeanPostProcessor processor : processors) {
bean =
processor
.postProcessBeforeInitialization(
bean,
beanName
);
}
10.7 @PostConstruct
现代 Spring 使用 Jakarta 注解:
java
@PostConstruct
public void init() {
}
它在:
text
依赖注入完成之后
执行。
所以适合:
text
依赖已经可用后
做轻量初始化
10.8 InitializingBean
java
public class UserService
implements InitializingBean {
@Override
public void afterPropertiesSet() {
}
}
优点:
text
明确
缺点:
text
业务类依赖 Spring 接口
普通业务更常用 @PostConstruct 或配置初始化方法。
10.9 init-method
Java 配置:
java
@Bean(
initMethod = "init"
)
public Client client() {
return new Client();
}
适合:
text
第三方类不能加 @PostConstruct
10.10 BeanPostProcessor After
初始化后处理器:
text
可能直接返回原对象
也可能返回包装/代理对象
AOP 自动代理创建器通常会在这个阶段判断:
text
这个 Bean 是否需要代理
因此:
text
容器最终保存和注入的对象
可能已经不是原始 target
而是 proxy
10.11 销毁阶段
单例 Bean 在 Context 关闭时可能调用:
text
@PreDestroy
DisposableBean#destroy()
destroy-method
适合:
text
关闭线程池
关闭连接
停止后台任务
释放本地资源
10.12 完整示例
java
@Component
public class LifeCycleBean
implements BeanNameAware,
InitializingBean,
DisposableBean {
public LifeCycleBean() {
System.out.println("1 constructor");
}
@Autowired
public void setUserService(
UserService userService) {
System.out.println("2 dependency injection");
}
@Override
public void setBeanName(
String name) {
System.out.println("3 BeanNameAware");
}
@PostConstruct
public void postConstruct() {
System.out.println("4 PostConstruct");
}
@Override
public void afterPropertiesSet() {
System.out.println(
"5 afterPropertiesSet");
}
@PreDestroy
public void preDestroy() {
System.out.println("destroy prepare");
}
@Override
public void destroy() {
System.out.println("destroy");
}
}
10.13 生产坑:@PostConstruct 做重 IO
错误:
java
@PostConstruct
public void init() {
remoteClient.queryAll();
}
问题:
text
远程接口 30 秒
↓
Bean 初始化阻塞 30 秒
↓
ApplicationContext refresh 被拖慢
↓
服务启动变慢
更严重的是远程服务挂掉:
text
整个应用可能无法启动
10.14 什么适合初始化阶段做
适合:
text
校验必要配置
构建本地小型只读映射
初始化轻量 SDK 状态
注册本地资源
不适合:
text
全表扫描
批量数据迁移
无限远程重试
大文件加载
大规模缓存预热
10.15 Prototype Bean 生命周期注意
Prototype Bean:
text
Spring 负责创建和初始化
但通常不会像 singleton 一样持续管理到完整销毁。
因此:
text
需要调用方自己管理某些销毁资源
这也是 prototype 不适合持有昂贵外部资源的原因之一。
[⬆ 返回总目录](#⬆ 返回总目录)
十一、Aware、初始化与销毁回调怎么选
11.1 业务 Bean 最推荐
普通初始化:
text
@PostConstruct
普通销毁:
text
@PreDestroy
优点:
text
直观
少耦合 Spring API
11.2 第三方类
如果无法修改第三方源码:
java
@Bean(
initMethod = "start",
destroyMethod = "close"
)
public ThirdPartyClient client() {
return new ThirdPartyClient();
}
非常适合。
11.3 框架类
如果正在写框架:
text
InitializingBean
BeanFactoryAware
ApplicationContextAware
可能更合适,因为框架本身本来就依赖 Spring。
11.4 避免 ApplicationContext.getBean 滥用
有些代码:
java
@Component
public class SpringContextHolder
implements ApplicationContextAware {
static ApplicationContext context;
}
然后业务到处:
java
SpringContextHolder.getBean(...)
这相当于:
text
把依赖注入重新变成 Service Locator
会隐藏依赖,不利于测试。
应该只在:
text
非常特殊的框架扩展
无法通过普通注入解决
时使用。
[⬆ 返回总目录](#⬆ 返回总目录)
十二、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor、BeanPostProcessor
这三个名字很像,但处理阶段完全不同。
12.1 BeanFactoryPostProcessor
作用:
text
Bean 实例化之前
修改 BeanDefinition
接口思想:
java
void postProcessBeanFactory(
ConfigurableListableBeanFactory factory);
12.2 使用场景
例如:
text
批量修改 BeanDefinition 属性
动态改变 Scope
读取自定义元数据
框架配置处理
12.3 BeanDefinitionRegistryPostProcessor
它比普通 BeanFactoryPostProcessor 更早、更强。
因为可以直接拿:
text
BeanDefinitionRegistry
动态:
text
注册新的 BeanDefinition
12.4 经典实现
非常重要:
text
ConfigurationClassPostProcessor
它解析:
text
@Configuration
@ComponentScan
@Import
@Bean
所以 Spring Java Config 能工作,背后不是"注解自动变 Bean"的魔法。
12.5 BeanPostProcessor
作用对象不是:
text
BeanDefinition
而是:
text
已经实例化的 Bean 对象
12.6 对比
text
BeanDefinitionRegistryPostProcessor
↓
增加/修改 BeanDefinition
BeanFactoryPostProcessor
↓
修改已有 BeanDefinition
BeanPostProcessor
↓
处理 Bean 实例
12.7 为什么 BeanPostProcessor 很强
因为它可以:
text
读取 Bean
修改 Bean
包装 Bean
甚至返回另外一个对象
AOP 代理就是这种能力的重要应用。
12.8 常见重要 BeanPostProcessor
包括:
text
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
AbstractAutoProxyCreator 体系
分别与:
text
@Autowired
@PostConstruct/@PreDestroy
AOP 自动代理
等机制相关。
12.9 自定义 BeanPostProcessor 示例
java
@Component
public class CostBeanPostProcessor
implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(
Object bean,
String beanName) {
System.out.println(
"before init: " + beanName
);
return bean;
}
}
12.10 为什么业务项目不要乱写全局 BPP
因为:
text
它可能作用于大量 Bean
如果里面:
text
做远程请求
持锁
修改对象
抛异常
会影响整个容器。
[⬆ 返回总目录](#⬆ 返回总目录)
十三、FactoryBean:为什么接口没有实现类也能注入代理对象
13.1 BeanFactory 与 FactoryBean 不要混
text
BeanFactory
= Spring IoC 容器接口
FactoryBean
= 一种特殊 Bean
13.2 FactoryBean 的目的
某些对象创建复杂:
text
动态代理
第三方 SDK
Mapper
RPC Client
无法简单:
java
new Xxx()
FactoryBean 可以封装创建逻辑。
13.3 核心方法
概念上:
java
public interface FactoryBean<T> {
T getObject();
Class<?> getObjectType();
boolean isSingleton();
}
13.4 获取 FactoryBean 产物
假设 Bean 名:
text
userMapper
java
context.getBean("userMapper")
拿到通常是:
text
FactoryBean.getObject()
返回的产物。
13.5 获取 FactoryBean 自己
java
context.getBean("&userMapper")
前缀:
text
&
表示:
text
我要工厂 Bean 本身
13.6 MyBatis 为什么 Mapper 接口没实现类也能注入
核心思想:
text
Mapper 接口
↓
MapperFactoryBean
↓
getObject()
↓
动态代理对象
所以:
java
@Autowired
private UserMapper userMapper;
虽然没有:
java
class UserMapperImpl
仍可以工作。
13.7 适合场景
FactoryBean 很适合:
text
代理对象
复杂客户端
对象创建需要大量步骤
框架生成对象
13.8 不适合
普通业务 Bean:
java
@Service
public class UserService
没有必要绕一层 FactoryBean。
[⬆ 返回总目录](#⬆ 返回总目录)
十四、Bean Scope 与单例 Bean 线程安全
14.1 Scope 是什么
Scope 决定:
text
Bean 创建多少份
生命周期多长
14.2 singleton
默认 Scope:
text
singleton
含义是:
text
一个 Spring 容器内
一个 beanName 通常一个实例
注意:
text
不是 JVM 绝对全局单例
两个 ApplicationContext:
text
可以各自有一份
14.3 prototype
java
@Scope("prototype")
@Component
public class TaskContext {
}
每次获取:
java
context.getBean(TaskContext.class)
通常会创建新实例。
14.4 Web Scope
常见:
text
request
session
application
websocket
它们依赖 Web ApplicationContext。
14.5 单例 Bean 线程安全吗
不保证。
Spring 保证的主要是:
text
实例数量语义
不是:
text
字段访问线程安全
14.6 错误例子
java
@Service
public class UserService {
private Long currentUserId;
public void process(Long userId) {
currentUserId = userId;
// ...
}
}
请求 A:
text
userId = 1
请求 B 同时:
text
userId = 2
字段会互相覆盖。
14.7 为什么大多数 Service 又没问题
因为通常是:
text
无状态
例如:
java
public UserVO getUser(Long id) {
User user =
repository.findById(id);
return convert(user);
}
id、user:
text
方法局部变量
每个线程都有自己的调用栈。
14.8 什么字段通常安全
text
final 依赖
不可变配置
线程安全组件
只读常量
14.9 什么字段危险
text
当前用户
临时 List
可变 Map
计数器
请求状态
14.10 Prototype 注入 singleton 的坑
如果 singleton Bean:
java
@Service
public class OrderService {
private final PrototypeBean prototype;
public OrderService(
PrototypeBean prototype) {
this.prototype = prototype;
}
}
构造 OrderService 时:
text
只解析一次 PrototypeBean
后面字段仍然是同一个引用。
如果希望:
text
每次调用都获取新的 prototype
需要:
text
ObjectProvider
Lookup Method
其他 Provider 机制
[⬆ 返回总目录](#⬆ 返回总目录)
十五、循环依赖与三级缓存
这是 Spring 面试最经典、也最容易背错的主题。
15.1 什么叫循环依赖
java
@Service
public class AService {
@Autowired
private BService bService;
}
java
@Service
public class BService {
@Autowired
private AService aService;
}
依赖图:
text
A → B
↑ ↓
└───┘
15.2 为什么会死循环
如果没有提前暴露机制:
text
创建 A
↓
A 需要 B
↓
创建 B
↓
B 需要 A
↓
创建 A
↓
A 又需要 B
...
无法结束。
15.3 Spring 处理的是哪些循环依赖
经典三级缓存主要针对:
text
单例 Bean
实例已经创建但初始化尚未完成
setter/字段等依赖注入场景
它不是:
text
万能循环依赖解决器
15.4 三个核心缓存
核心类:
text
DefaultSingletonBeanRegistry
概念上有:
text
singletonObjects
earlySingletonObjects
singletonFactories
15.5 一级缓存 singletonObjects
存放:
text
完整创建完成的单例 Bean
正常:
java
getBean()
首先希望从这里直接拿。
15.6 二级缓存 earlySingletonObjects
存放:
text
已经被其他 Bean 提前引用的早期 Bean
它还没有走完整生命周期。
15.7 三级缓存 singletonFactories
存放:
text
ObjectFactory
不是直接 Bean。
这个工厂在真正需要早期引用时:
text
可以决定返回什么
15.8 A → B → A 完整流程
第一步:实例化 A
text
createBeanInstance(A)
A 已经有 Java 对象:
text
但还没注入 B
第二步:A 加入三级缓存
Spring 放入:
text
A → ObjectFactory
ObjectFactory 的职责:
text
如果有人提前需要 A
生成 A 的 early reference
第三步:A 开始 populateBean
发现:
text
需要 B
于是:
text
getBean(B)
第四步:创建 B
B 实例化后开始注入属性。
发现:
text
需要 A
于是:
text
getBean(A)
第五步:查 A
text
一级缓存:没有
二级缓存:没有
三级缓存:有 ObjectFactory
调用 Factory:
text
得到 A 的 early reference
然后:
text
放二级缓存
三级缓存对应工厂移除
第六步:B 注入 A
B 成功拿到:
text
A 的早期引用
然后 B 完成初始化。
进入:
text
一级缓存
第七步:A 注入 B
B 现在已经完整。
A 注入 B。
然后 A 继续初始化。
最终进入:
text
一级缓存
15.9 为什么需要二级缓存
如果每次 B、C 都来依赖 A:
text
不能每次都重新调用 Factory
重新生成不同 early proxy
第一次生成 early reference 后放二级缓存:
text
后续都返回同一个 early reference
15.10 为什么需要三级缓存
经典面试追问:
二级缓存已经能放半成品 A,为什么还要三级?
关键不是:
text
"为了多缓存一层"
而是:
text
三级缓存保存的是"生成早期引用的工厂"
这样 Spring 可以延迟到:
text
真的有人循环依赖 A
时,再决定:
text
直接给原始 A
还是经过 getEarlyBeanReference()
得到提前代理对象
这与 AOP 代理对象一致性密切相关。
15.11 getEarlyBeanReference
AOP 自动代理创建器可以参与:
text
early reference
如果 A 最终需要被代理:
text
B 不能拿到原始 A
而容器最终却给其他人代理 A
否则同一个逻辑 Bean 会出现:
text
两个不同引用语义
三级缓存提供了延迟代理机会。
15.12 为什么不是所有 AOP + 循环依赖都完美处理
真实场景比:
text
A → B → A
复杂。
如果存在:
text
多个 BeanPostProcessor
代理包装顺序
异步代理
特殊 TargetSource
动态增强
可能导致:
text
raw bean 注入与最终代理不一致
Spring 也会检测部分这种问题。
所以不要把:
text
"三级缓存能解决循环依赖"
理解成:
text
所有复杂代理循环依赖都安全
15.13 构造器循环依赖为什么不行
java
class A {
A(B b) {}
}
java
class B {
B(A a) {}
}
创建 A 时:
text
必须先拿到 B
A 自己甚至:
text
还没有完成实例化
因此没有:
text
A 半成品对象
可以放三级缓存。
15.14 prototype 循环依赖
Prototype 每次都要创建新对象,没有 singleton 缓存生命周期模型,所以经典三级缓存方案不能像 singleton 那样解决。
15.15 @Lazy 能不能"解决"
例如:
java
public AService(
@Lazy BService bService) {
}
Spring 可以注入:
text
延迟代理
把真正解析推迟到使用时。
但这通常是:
text
缓解依赖结构
不是证明设计合理。
15.16 最推荐的解决方式
优先:
text
重新拆职责
例如:
text
OrderService ↔ CouponService
可以抽:
text
OrderCouponCoordinator
或:
text
领域服务
事件
15.17 事件解耦例子
原来:
text
OrderService
调用
PointService
PointService 又回调 OrderService。
可以:
text
OrderService
↓
发布 OrderCreatedEvent
↓
PointListener
解除直接双向依赖。
15.18 面试复盘
Q:Spring 为什么有三级缓存?
推荐:
一级缓存保存完整单例,二级缓存保存已经产生过的早期引用,三级缓存保存能够生成早期引用的 ObjectFactory。三级缓存的关键价值不是简单解决普通循环依赖,而是延迟产生 early reference,使 AOP 自动代理创建器有机会在循环依赖场景中返回与最终 Bean 一致的代理引用。
[⬆ 返回总目录](#⬆ 返回总目录)
十六、AOP 核心概念
16.1 AOP 是什么
AOP:
text
Aspect-Oriented Programming
面向切面编程
解决的是:
text
横切关注点
例如:
text
日志
事务
权限
监控
审计
缓存
重试
幂等
16.2 为什么 OOP 不够
假设 100 个 Service 方法都要:
text
记录耗时
如果每个方法:
java
long start = ...;
try {
business();
} finally {
logCost();
}
业务代码被基础设施污染。
AOP 可以把:
text
"哪些方法要增强"
和:
text
"增强做什么"
集中管理。
16.3 Aspect
切面:
text
横切逻辑的模块化封装
例如:
java
@Aspect
@Component
public class AuditAspect {
}
16.4 Join Point
连接点。
在 Spring AOP 中核心可以理解为:
text
方法执行
Spring AOP 不是任意 Java 指令级织入。
16.5 Pointcut
切点:
text
哪些方法需要增强
例如:
java
execution(
* com.demo.service..*(..)
)
16.6 Advice
增强逻辑:
text
Before
After
AfterReturning
AfterThrowing
Around
16.7 Target
真实业务对象。
例如:
text
OrderService 原始对象
16.8 Proxy
对外暴露的代理对象。
调用:
text
proxy.createOrder()
代理先执行增强:
text
事务
日志
权限
再调用 Target。
16.9 Advisor
Spring 底层更常用的组合概念:
text
Advisor = Pointcut + Advice
表示:
text
什么地方
执行什么增强
16.10 Around 示例
java
@Aspect
@Component
public class CostAspect {
@Around(
"@annotation(CostTime)")
public Object around(
ProceedingJoinPoint pjp)
throws Throwable {
long start =
System.nanoTime();
try {
return pjp.proceed();
} finally {
long cost =
System.nanoTime() - start;
System.out.println(
pjp.getSignature()
+ " cost=" + cost);
}
}
}
16.11 适合 AOP
特别适合:
text
横跨多个模块
逻辑相对统一
和业务主体无关
例如:
text
日志
事务
权限
审计
耗时
16.12 不适合 AOP
不要把真正业务流程藏进切面:
text
扣库存
创建订单
计算价格
发送核心指令
否则调用关系:
text
肉眼看不到
可维护性极差。
[⬆ 返回总目录](#⬆ 返回总目录)
十七、Spring AOP 代理创建源码主线
17.1 谁负责自动代理
非常重要的体系:
text
AbstractAutoProxyCreator
它本身属于:
text
BeanPostProcessor
更具体地说会参与 Bean 实例化前后阶段。
17.2 为什么 AOP 能在 Bean 生命周期里生效
因为 Bean 创建:
text
initializeBean()
↓
BeanPostProcessor after
时,自动代理创建器可以判断:
text
这个 Bean 是否匹配某些 Advisor
如果匹配:
text
创建 Proxy
最终返回:
text
代理对象
17.3 主线
简化:
text
Bean 创建
↓
postProcessAfterInitialization
↓
wrapIfNecessary
↓
寻找匹配 Advisor
↓
ProxyFactory
↓
创建 AopProxy
↓
JDK Proxy / CGLIB
↓
返回代理
17.4 wrapIfNecessary
核心思想:
text
这个 Bean 是否应该被代理?
它会检查:
text
基础设施类?
已经处理过?
有没有匹配 Advisor?
如果没有:
text
返回原 Bean
如果有:
text
createProxy()
17.5 ProxyFactory
ProxyFactory 汇总:
text
Target
Advisor
接口
代理配置
再选择合适的 AopProxy。
17.6 为什么一个 Bean 有时不是原类对象
例如:
java
OrderService bean =
context.getBean(OrderService.class);
如果被 AOP:
text
bean 可能是代理对象
所以:
text
bean.getClass()
可能看到:
text
JDK Proxy
或 CGLIB 生成类
17.7 生产排障
如果:
text
AOP 注解没生效
先看:
text
Bean 是否 Spring 管理
是否真的生成代理
方法是否匹配 Pointcut
调用是否经过代理
代理方式是否支持该方法
[⬆ 返回总目录](#⬆ 返回总目录)
十八、JDK 动态代理与 CGLIB
18.1 JDK 动态代理
基于:
java
java.lang.reflect.Proxy
InvocationHandler
通常围绕:
text
接口
创建代理。
18.2 示例
java
UserService proxy =
(UserService)
Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
(p, method, args) -> {
System.out.println("before");
Object result =
method.invoke(target, args);
System.out.println("after");
return result;
}
);
18.3 调用流程
text
proxy.query()
↓
InvocationHandler.invoke()
↓
增强逻辑
↓
反射调用 target.query()
18.4 CGLIB
核心思想:
text
生成目标类的子类
然后通过:
text
重写可代理方法
插入拦截逻辑。
18.5 为什么 final 有限制
子类代理依赖:
text
继承
方法覆盖
所以:
text
final class
不能继承
final method
不能 override
自然难以通过子类代理增强。
18.6 private 方法为什么不适合代理增强
private 方法:
text
不能被子类正常 override
而且代理模型本身主要围绕:
text
外部方法调用
因此不要期待:
text
给 private 方法加 @Transactional
能像 public Service 方法一样工作。
18.7 JDK 和 CGLIB 谁更快
不要背:
text
"JDK 一定慢,CGLIB 一定快"
现代 JVM 优化下实际差异通常不是业务主要瓶颈。
选型首先看:
text
代理模型
类型兼容性
框架默认行为
而不是微小调用开销。
[⬆ 返回总目录](#⬆ 返回总目录)
十九、Advice、Advisor、Pointcut 与拦截器链
19.1 一次代理调用不是只有一个切面
同一个方法可能同时有:
text
事务
日志
权限
缓存
监控
所以实际是:
text
Interceptor Chain
19.2 MethodInterceptor
底层增强常被统一适配成:
text
MethodInterceptor
核心思想:
java
Object invoke(
MethodInvocation invocation)
内部:
java
return invocation.proceed();
表示:
text
继续执行下一个拦截器
19.3 调用链
text
Proxy
↓
SecurityInterceptor
↓
TransactionInterceptor
↓
LogInterceptor
↓
Target Method
返回时反向退出。
19.4 为什么 @Around 很强
因为它控制:
text
proceed() 前
proceed() 后
异常
返回值
是否继续执行
所以功能最强。
19.5 但 Around 也最危险
错误切面:
java
@Around(...)
public Object around(...) {
try {
return pjp.proceed();
} catch (Exception e) {
return null;
}
}
这会:
text
吞掉异常
可能进一步导致:
text
事务无法按预期回滚
调用方误认为成功
19.6 Advisor 顺序
多个增强顺序可通过:
text
Ordered
@Order
等机制控制。
但不要过度依赖非常复杂的顺序耦合。
如果:
text
切面 A 必须在 B 前
B 必须在 C 前
C 又依赖 A 状态
说明设计可能太隐式。
[⬆ 返回总目录](#⬆ 返回总目录)
二十、AOP 自调用失效与生产坑
20.1 经典问题
java
@Service
public class OrderService {
public void createOrder() {
this.saveOrder();
}
@Transactional
public void saveOrder() {
}
}
很多情况下:
text
saveOrder 事务不生效
20.2 根本原因
外部调用:
text
Controller
↓
OrderService Proxy
↓
TransactionInterceptor
↓
Target
内部调用:
text
Target.createOrder()
↓
this.saveOrder()
这里:
text
this = Target 自己
没有重新经过 Proxy。
因此:
text
TransactionInterceptor 没执行
20.3 不只是 @Transactional
同样影响典型:
text
@Async
@Cacheable
自定义 AOP 注解
只要依赖代理拦截:
text
绕过代理就可能失效
20.4 推荐解决
最清晰:
text
拆成两个 Bean
例如:
java
@Service
public class OrderService {
private final OrderTxService txService;
public void create() {
txService.save();
}
}
20.5 不推荐为了绕过问题滥用代理自取
例如:
text
AopContext.currentProxy()
虽然某些场景可以使用,但业务代码依赖:
text
当前一定存在代理上下文
耦合更强。
[⬆ 返回总目录](#⬆ 返回总目录)
二十一、Spring 声明式事务完整原理
21.1 什么叫事务
数据库事务核心目标:
text
一组数据库操作
要么共同成功
要么按规则回滚
例如转账:
sql
UPDATE account
SET balance = balance - 100
WHERE id = 1;
UPDATE account
SET balance = balance + 100
WHERE id = 2;
不能:
text
只扣不加
21.2 没有 Spring 时
JDBC:
java
Connection conn =
dataSource.getConnection();
try {
conn.setAutoCommit(false);
updateA(conn);
updateB(conn);
conn.commit();
} catch (Exception e) {
conn.rollback();
throw e;
} finally {
conn.close();
}
大量样板代码。
21.3 Spring 事务解决什么
Spring 抽象:
text
事务管理器
事务定义
事务状态
资源同步
再通过 AOP:
text
把事务边界自动套在业务方法外面
21.4 @Transactional
java
@Transactional
public void transfer(
long from,
long to,
BigDecimal amount) {
debit(from, amount);
credit(to, amount);
}
开发者只写:
text
事务语义
不再手动 commit/rollback。
21.5 底层主线
text
调用 Service Proxy
↓
TransactionInterceptor
↓
TransactionAspectSupport
↓
获取 TransactionManager
↓
getTransaction()
↓
开启/加入事务
↓
invocation.proceed()
↓
目标业务方法
↓
成功 → commit
异常 → rollback 判断
21.6 TransactionInterceptor
它本质是一个:
text
MethodInterceptor
说明:
text
声明式事务就是 AOP 拦截器
21.7 PlatformTransactionManager
经典抽象:
text
PlatformTransactionManager
核心动作:
text
getTransaction
commit
rollback
现代 Spring 事务 API 也在持续演进,但这个抽象仍是理解传统命令式事务的重要核心。
21.8 DataSourceTransactionManager
JDBC 场景典型管理器会:
text
从 DataSource 获取 Connection
关闭 autoCommit
绑定当前线程
提交/回滚
释放资源
21.9 为什么一个事务里的 JDBC 操作能共享连接
关键:
text
TransactionSynchronizationManager
它会把资源和当前线程关联。
可以简化理解:
text
ThreadLocal
↓
DataSource → ConnectionHolder
所以同一线程中:
text
JdbcTemplate
MyBatis-Spring
能获取到事务上下文中的连接。
21.10 注意:ThreadLocal 不等于"事务就是 ThreadLocal"
准确说:
text
数据库事务最终还是数据库/JDBC Connection 实现
Spring 使用线程绑定机制:
text
管理当前调用线程对应的事务资源
21.11 事务与数据库的关系
Spring:
text
管理事务边界
统一编程模型
决定传播
决定回滚
绑定资源
数据库:
text
真正完成 commit/rollback
MVCC
锁
undo
redo
隔离性
不要混淆。
21.12 事务适合场景
text
订单主表 + 明细
转账
库存扣减
批量状态更新
业务状态与操作日志同库强一致
21.13 本地事务不解决什么
一个本地数据库事务不能自动保证:
text
MySQL
+
Redis
+
Kafka
+
第三方 HTTP
四者原子提交。
这属于:
text
分布式一致性
需要其他方案。
[⬆ 返回总目录](#⬆ 返回总目录)
二十二、TransactionManager、TransactionInterceptor 与线程资源绑定
22.1 TransactionManager 是什么
事务管理器负责把 Spring 的统一事务语义映射到具体资源。
例如:
text
JDBC
JPA
JTA
R2DBC
不同技术底层事务 API 不同,Spring 用统一抽象屏蔽差异。
22.2 TransactionDefinition
描述:
text
这个事务想要什么规则
包括:
text
传播行为
隔离级别
超时
只读
名称
22.3 TransactionStatus
描述:
text
当前事务运行状态
例如:
text
是不是新事务
是否标记 rollback-only
是否完成
22.4 事务拦截器完整思路
text
拿到 Method
↓
读取 @Transactional 元数据
↓
找到 TransactionManager
↓
根据 propagation 判断:
新建?
加入?
挂起?
不开?
↓
执行目标方法
↓
正常返回 → commit
异常 → rollback rule
↓
恢复被挂起事务
↓
清理线程资源
22.5 TransactionSynchronizationManager
常见管理内容:
text
resources
synchronizations
transactionName
readOnly
isolationLevel
active flag
这些通常以线程上下文形式维护。
22.6 为什么多线程会打断事务上下文
主线程:
text
Thread-1
DataSource → Connection-A
你突然:
java
executor.submit(
() -> repository.update(...)
);
子线程:
text
Thread-2
没有 Thread-1 的绑定资源。
它可能:
text
重新获取 Connection-B
自然不在原事务。
22.7 生产场景
假设:
text
事务中批量查 10 万条数据
然后开 20 个线程更新
不要误以为:
text
这 20 个线程天然属于同一个 @Transactional
如果需要并发数据库操作,必须重新设计事务边界和一致性方案。
[⬆ 返回总目录](#⬆ 返回总目录)
二十三、事务传播行为
Propagation 解决:
text
事务方法 A 调用事务方法 B
B 到底用哪个事务?
23.1 REQUIRED
默认。
text
当前有事务 → 加入
当前没事务 → 新建
业务:
text
createOrder()
↓
saveOrder()
↓
saveDetail()
通常希望:
text
一个事务
适合 REQUIRED。
23.2 REQUIRES_NEW
text
总是需要独立新事务
如果外层有事务:
text
外层挂起
↓
内层开新事务
↓
内层结束
↓
恢复外层
23.3 真实场景:操作日志
订单事务失败:
text
业务回滚
但希望:
text
失败审计日志仍然提交
可以考虑把日志服务做成单独 Bean:
java
@Transactional(
propagation =
Propagation.REQUIRES_NEW
)
public void saveAuditLog(...) {
}
23.4 REQUIRES_NEW 的大坑
外层事务已经占:
text
一个 Connection
内层新事务还要:
text
另一个 Connection
高并发 + 小连接池时:
text
连接池可能耗尽
所以不要到处使用。
23.5 SUPPORTS
text
有事务 → 加入
没事务 → 非事务执行
适合:
text
可在事务上下文中参与
但自身不强制需要事务
23.6 MANDATORY
text
必须已有事务
否则报错
适合:
text
明确要求只能作为某事务流程内部步骤
但使用频率不高。
23.7 NOT_SUPPORTED
text
必须非事务执行
有外层事务则挂起
适合:
text
明确不希望长期操作占用事务上下文
23.8 NEVER
text
当前有事务就报错
用于强约束。
23.9 NESTED
嵌套事务。
在支持 Savepoint 的传统 JDBC 事务管理场景中,可以理解为:
text
外层事务
↓
创建 Savepoint
↓
内层失败可以回滚到 Savepoint
23.10 NESTED 与 REQUIRES_NEW 的区别
REQUIRES_NEW
text
两个独立数据库事务
内层提交后:
text
外层以后回滚
内层通常仍已提交
NESTED
通常:
text
仍在同一个物理事务里
使用 Savepoint
最终外层整体回滚:
text
嵌套部分也无法独立存活
23.11 注意事务管理器能力
不是所有事务管理器都支持相同的 NESTED 语义。
不要只背注解枚举。
实际要看:
text
底层资源
TransactionManager 实现
[⬆ 返回总目录](#⬆ 返回总目录)
二十四、事务隔离级别、readOnly、timeout、rollbackFor
24.1 Isolation 是什么
Spring 可以声明:
java
@Transactional(
isolation =
Isolation.READ_COMMITTED
)
但是:
text
真正隔离行为由数据库实现
24.2 常见隔离级别
text
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE
DEFAULT
24.3 DEFAULT
表示:
text
使用底层数据库/数据源默认隔离级别
24.4 不要把 Spring Isolation 和 MVCC 混在一起
Spring 只是:
text
向底层 Connection/事务系统传递隔离要求
数据库的:
text
MVCC
Read View
锁
undo
才决定具体读现象。
24.5 readOnly
java
@Transactional(readOnly = true)
public UserVO query(...) {
}
它表达:
text
这个事务主要用于读取
但不要误解成:
text
"数据库绝对禁止任何 UPDATE"
具体优化/约束取决于:
text
TransactionManager
数据库
ORM
驱动
它更多是:
text
语义 + 优化提示
24.6 timeout
java
@Transactional(timeout = 3)
表示:
text
事务允许的超时时间语义
但具体超时传播到:
text
数据库操作
查询
锁等待
的行为需结合具体事务管理器和数据访问框架。
24.7 rollbackFor
Spring 默认回滚规则主要针对:
text
RuntimeException
Error
如果业务使用受检异常:
java
@Transactional(
rollbackFor = Exception.class
)
可以显式声明。
24.8 noRollbackFor
某类异常:
text
业务上允许事务提交
可以:
java
@Transactional(
noRollbackFor =
SomeBizException.class
)
但应谨慎。
24.9 rollback-only
如果内层代码把事务标记:
text
rollback-only
即使外层最后没有异常,也可能在提交阶段发现:
text
不能提交
出现:
text
UnexpectedRollbackException
24.10 UnexpectedRollbackException 典型场景
text
外层 REQUIRED
↓
内层 REQUIRED
↓
内层抛 RuntimeException
↓
事务被标记 rollback-only
↓
外层 catch 异常并继续
↓
外层以为能 commit
↓
实际提交发现 rollback-only
↓
UnexpectedRollbackException
这是非常经典的面试场景。
[⬆ 返回总目录](#⬆ 返回总目录)
二十五、@Transactional 失效场景
不要只背"八种失效"。
先记根本原则:
text
声明式事务依赖:
Spring Bean
+
事务 Advisor
+
代理调用
+
正确事务资源
+
正确异常/传播规则
25.1 同类自调用
java
this.txMethod();
绕过代理。
25.2 对象不是 Spring Bean
java
OrderService service =
new OrderService(...);
不会自动有 Spring 事务代理。
25.3 方法不可被当前代理模式有效拦截
实践中事务方法通常推荐:
text
public
并避免:
text
private/final 等容易与代理限制冲突的设计
具体规则需结合当前 Spring 版本和代理模式。
25.4 异常被 catch 吃掉
java
@Transactional
public void create() {
try {
repository.insert();
throw new RuntimeException();
} catch (Exception e) {
log.error("failed", e);
}
}
外层拦截器看到:
text
正常返回
可能提交。
25.5 回滚异常类型不匹配
受检异常:
java
throw new IOException();
如果没有配置:
text
rollbackFor
可能不符合默认回滚规则。
25.6 多线程
java
@Transactional
public void process() {
executor.submit(
() -> repository.update()
);
}
子线程不天然继承当前事务。
25.7 数据库/存储引擎不支持事务
Spring 只能:
text
调用底层事务 API
底层没有能力,Spring 也无法凭空提供。
25.8 错误传播行为
例如:
text
内层 REQUIRES_NEW
已经独立提交。
外层后来回滚:
text
不能自动把内层已提交事务撤销
25.9 事务范围太大
这不叫"失效",但生产危害很大。
java
@Transactional
public void process() {
query();
remoteCall(); // 8s
sleep();
update();
}
造成:
text
连接长期占用
锁长期持有
吞吐下降
死锁概率上升
25.10 正确边界原则
事务里尽量:
text
只做必须保持原子性的数据库操作
远程调用、慢 IO 应尽量:
text
移出事务
或者重新设计一致性
[⬆ 返回总目录](#⬆ 返回总目录)
二十六、Spring MVC 整体架构与请求链路
Spring MVC 是 Spring Framework 的 Servlet Web MVC 框架。
26.1 核心思想
使用:
text
Front Controller
也就是:
text
DispatcherServlet
所有 MVC 请求统一先进入一个前端控制器。
26.2 为什么需要 Front Controller
如果每个 Servlet 自己处理:
text
URL 映射
参数绑定
JSON
异常
视图
拦截器
框架能力会非常分散。
统一 DispatcherServlet 后:
text
请求分发和扩展点集中管理
26.3 完整请求链
text
HTTP Request
↓
Servlet Container
↓
Filter Chain
↓
DispatcherServlet
↓
HandlerMapping
↓
HandlerExecutionChain
↓
Interceptor preHandle
↓
HandlerAdapter
↓
ArgumentResolver
↓
Controller Method
↓
Service
↓
ReturnValueHandler
↓
HttpMessageConverter / View
↓
Interceptor
↓
HTTP Response
异常时:
text
HandlerExceptionResolver
参与。
26.4 DispatcherServlet 不是业务 Controller
它本身是:
text
调度中心
不处理业务。
职责:
text
找 Handler
找 Adapter
调 Handler
处理 ModelAndView
异常解析
视图渲染
[⬆ 返回总目录](#⬆ 返回总目录)
二十七、DispatcherServlet、HandlerMapping、HandlerAdapter
27.1 HandlerMapping
负责:
text
"这个请求应该找谁处理?"
例如:
java
@GetMapping("/orders/{id}")
public OrderVO get(
@PathVariable Long id) {
}
HandlerMapping 会根据:
text
URL
HTTP Method
headers
params
consumes
produces
等条件匹配 HandlerMethod。
27.2 RequestMappingHandlerMapping
注解 Controller 的核心 Mapping 实现之一。
启动时它会扫描:
text
@Controller
@RequestMapping
@GetMapping
@PostMapping
建立:
text
RequestMappingInfo
→ HandlerMethod
映射表。
27.3 HandlerExecutionChain
除了 Handler,还带:
text
Interceptor 列表
所以执行时能:
text
preHandle
postHandle
afterCompletion
27.4 HandlerAdapter
为什么已经找到 Controller Method 还要 Adapter?
因为 DispatcherServlet 希望:
text
不关心具体 Handler 怎么调用
于是使用适配器。
注解 Controller 典型由:
text
RequestMappingHandlerAdapter
处理。
27.5 HandlerAdapter 内部做了很多事
包括:
text
参数解析
数据绑定
校验
调用 Controller
处理返回值
所以"调用 Controller 方法"并不是简单:
java
method.invoke(...)
27.6 生产排障
接口根本进不到 Controller:
先查:
text
Filter
Mapping
Interceptor preHandle
Content-Type
请求方法
路径
不要只在 Controller 打断点。
[⬆ 返回总目录](#⬆ 返回总目录)
二十八、参数解析、数据绑定与 HttpMessageConverter
28.1 参数解析器是什么
假设:
java
@GetMapping("/users/{id}")
public UserVO get(
@PathVariable Long id,
@RequestHeader("X-Tenant") String tenant) {
}
Spring 必须知道:
text
Long id 从哪里来?
tenant 从哪里来?
这由:
text
HandlerMethodArgumentResolver
体系完成。
28.2 常见参数来源
text
@RequestParam
@PathVariable
@RequestHeader
@CookieValue
@RequestBody
ModelAttribute
ServletRequest
Principal
各自有不同 resolver。
28.3 @RequestParam
请求:
text
GET /users?page=1&size=20
Controller:
java
public List<UserVO> list(
@RequestParam int page,
@RequestParam int size) {
}
28.4 @PathVariable
text
GET /users/100
java
@GetMapping("/users/{id}")
public UserVO get(
@PathVariable Long id) {
}
28.5 @RequestBody
请求 Body:
json
{
"name": "Tom",
"age": 20
}
Controller:
java
@PostMapping("/users")
public UserVO create(
@RequestBody CreateUserRequest req) {
}
28.6 HttpMessageConverter
负责:
text
HTTP Body
↔
Java Object
典型 JSON converter 会使用 JSON 库做:
text
序列化
反序列化
28.7 为什么 Content-Type 错会 415
如果 Controller 要:
text
application/json
客户端却发:
text
text/plain
找不到合适的消息转换器/媒体类型匹配,就可能返回:
text
415 Unsupported Media Type
28.8 为什么 Accept 可能导致 406
客户端:
text
Accept: application/xml
服务端只有 JSON 输出能力。
可能:
text
406 Not Acceptable
28.9 返回值处理
Controller:
java
@RestController
public UserVO get() {
return new UserVO(...);
}
不是 Controller 自己:
text
手动 JSON.stringify
而是:
text
HandlerMethodReturnValueHandler
↓
HttpMessageConverter
↓
Response Body
28.10 生产坑:返回 Entity
直接:
java
return userEntity;
风险:
text
暴露数据库字段
API 与表结构耦合
ORM 懒加载序列化问题
敏感字段泄露
建议:
text
Request DTO
Command
Domain/Entity
Response VO
按职责分离。
[⬆ 返回总目录](#⬆ 返回总目录)
二十九、Validation 参数校验
29.1 Validation 是什么
Spring 集成 Jakarta Bean Validation。
典型:
java
public class CreateUserRequest {
@NotBlank
private String name;
@Min(1)
@Max(150)
private Integer age;
@Email
private String email;
}
Controller:
java
public void create(
@Valid
@RequestBody CreateUserRequest req) {
}
29.2 为什么需要统一校验
避免:
java
if (name == null) ...
if (age < 1) ...
散落整个系统。
29.3 适合做
text
非空
长度
数字范围
正则
Email
格式约束
29.4 不适合替代业务校验
例如:
text
库存是否足够
订单状态能否取消
用户是否属于该部门
优惠券是否过期且属于当前账户
这些需要:
text
查数据库/领域状态
属于业务规则。
29.5 嵌套校验
DTO:
java
class OrderRequest {
@Valid
@NotNull
private Address address;
}
如果没有:
java
@Valid
嵌套对象内部校验可能不会级联执行。
29.6 分组校验
可用于:
text
新增
修改
不同约束。
但分组太多会:
text
让 DTO 规则非常复杂
复杂业务校验更适合显式业务代码。
[⬆ 返回总目录](#⬆ 返回总目录)
三十、全局异常处理
30.1 为什么需要统一处理
不推荐每个 Controller:
java
try {
} catch (...) {
}
会重复:
text
错误码
日志
响应
30.2 @RestControllerAdvice
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(
BizException.class)
public ApiResult<?> handle(
BizException e) {
return ApiResult.fail(
e.getCode(),
e.getMessage()
);
}
}
30.3 HandlerExceptionResolver
Spring MVC 异常体系核心抽象。
DispatcherServlet 调用 Handler 发生异常后:
text
遍历异常解析器
尝试将异常转换为:
text
ModelAndView
Response
状态码
30.4 典型异常分层
可以区分:
text
参数异常
业务异常
权限异常
系统异常
30.5 系统异常不要直接返回堆栈
客户端响应:
json
{
"code": "SYSTEM_ERROR",
"message": "系统繁忙,请稍后重试",
"traceId": "..."
}
服务端日志保留:
text
完整 stack trace
30.6 HTTP 状态码
不要所有错误都:
text
HTTP 200
除非公司协议明确要求。
REST 风格可合理使用:
text
400
401
403
404
409
500
并配合业务错误码。
[⬆ 返回总目录](#⬆ 返回总目录)
三十一、Filter、Interceptor、AOP 如何选
31.1 Filter
属于:
text
Servlet 规范
最靠近 Web 容器。
适合:
text
CORS
traceId
请求包装
Header
安全过滤
31.2 Interceptor
属于:
text
Spring MVC
知道:
text
Handler
Controller
适合:
text
登录状态
Controller 权限
MVC 层请求上下文
31.3 AOP
作用于:
text
Spring Bean 方法
不限定 HTTP。
适合:
text
Service 权限
数据权限
事务
审计
日志
监控
31.4 执行位置
大致:
text
Filter
↓
DispatcherServlet
↓
Interceptor
↓
Controller
↓
Service Proxy / AOP
31.5 场景选择
所有 HTTP 请求生成 traceId
选:
text
Filter
Controller 接口权限
选:
text
Interceptor
Service 方法数据权限
选:
text
AOP
[⬆ 返回总目录](#⬆ 返回总目录)
三十二、Spring Event 事件机制
32.1 是什么
Spring Event 是:
text
进程内发布/订阅机制
核心:
text
ApplicationEventPublisher
ApplicationListener
ApplicationEventMulticaster
32.2 例子
事件:
java
public record OrderCreatedEvent(
Long orderId) {
}
发布:
java
publisher.publishEvent(
new OrderCreatedEvent(orderId)
);
监听:
java
@EventListener
public void onOrderCreated(
OrderCreatedEvent event) {
// ...
}
32.3 为什么要事件
原来:
text
OrderService
├─ PointService
├─ SmsService
├─ AuditService
└─ StatisticsService
依赖越来越多。
事件:
text
OrderService
↓
OrderCreatedEvent
├─ PointListener
├─ AuditListener
└─ StatisticsListener
减少直接耦合。
32.4 默认同步还是异步
普通 Spring Event 默认通常:
text
同步调用监听器
所以:
text
监听器执行 5 秒
发布事件的方法也会被拖慢
32.5 异步事件
可以结合:
text
@Async
或配置异步事件广播策略。
但一旦异步:
text
异常传播
事务上下文
执行顺序
可靠性
都要重新考虑。
32.6 TransactionalEventListener
用于:
text
绑定事务阶段
例如:
java
@TransactionalEventListener(
phase =
TransactionPhase.AFTER_COMMIT
)
表示:
text
事务成功提交后再执行
32.7 真实场景
订单数据库事务:
text
保存订单
↓
提交成功
↓
清理某本地缓存/执行弱一致后续逻辑
可以考虑 AFTER_COMMIT。
32.8 不要把 Spring Event 当 MQ
Spring Event 通常:
text
进程内
无 Broker 持久化
服务重启不会重放
强可靠跨服务业务:
text
MQ
Outbox
本地消息表
更合适。
[⬆ 返回总目录](#⬆ 返回总目录)
三十三、Resource 资源抽象
33.1 为什么需要 Resource
Java 原生资源来源很多:
text
File
Classpath
URL
ServletContext
InputStream
Spring 统一抽象:
text
Resource
33.2 常见实现
text
ClassPathResource
FileSystemResource
UrlResource
ByteArrayResource
InputStreamResource
33.3 示例
java
Resource resource =
new ClassPathResource(
"templates/a.txt"
);
try (InputStream in =
resource.getInputStream()) {
}
33.4 ResourceLoader
可以按统一路径:
text
classpath:
file:
https:
加载资源。
33.5 真实场景
text
读取模板
加载证书
加载规则文件
测试资源
框架配置文件
33.6 不适合做什么
不要用 Resource 去模拟:
text
大型对象存储系统
几十 GB 文件、分片上传下载等应该用:
text
OSS/S3 SDK
[⬆ 返回总目录](#⬆ 返回总目录)
三十四、ConversionService、Formatter、DataBinder
这一组经常被忽略,但理解 MVC 参数绑定很有帮助。
34.1 ConversionService
统一类型转换。
例如:
text
String "100"
→ Long 100
核心 SPI:
text
Converter<S,T>
34.2 自定义 Converter
java
public class StringToOrderStatusConverter
implements Converter<
String,
OrderStatus> {
@Override
public OrderStatus convert(
String source) {
return OrderStatus.valueOf(
source.toUpperCase()
);
}
}
34.3 Formatter
更偏:
text
文本格式化
本地化
典型:
text
日期
金额
34.4 DataBinder
负责:
text
把请求/属性值绑定到对象属性
同时可配合:
text
ConversionService
Validator
34.5 WebDataBinder
Spring MVC 场景扩展。
可以:
text
设置允许绑定字段
注册 Editor/Formatter
34.6 安全注意:Mass Assignment
如果直接把所有请求字段绑定到 Entity:
text
客户端可能提交本不应该修改的字段
例如:
json
{
"name": "Tom",
"role": "ADMIN"
}
如果 Entity 有 role 且自动绑定:
text
可能形成越权
推荐使用:
text
专用 Request DTO
明确允许字段
[⬆ 返回总目录](#⬆ 返回总目录)
三十五、SpEL 表达式语言
35.1 SpEL 是什么
SpEL:
text
Spring Expression Language
可以动态访问:
text
对象属性
方法
集合
条件
Bean
35.2 示例
java
@Value("#{systemProperties['user.home']}")
private String home;
35.3 常见应用
Spring 生态中很多注解支持表达式:
text
@Cacheable key
Spring Security 表达式
条件逻辑
部分配置
35.4 Cache Key 示例
java
@Cacheable(
cacheNames = "user",
key = "#userId"
)
public User get(Long userId) {
}
#userId 就是表达式上下文的一部分。
35.5 不适合场景
不要把复杂业务规则全部塞进字符串表达式:
text
可读性差
重构不安全
IDE 支持弱
运行期才暴露错误
35.6 安全风险
如果允许:
text
不可信用户输入
被直接当成 SpEL 解析,可能产生严重安全风险。
不要设计:
text
用户输入表达式 → 直接 parser.parseExpression()
除非做了严格沙箱和白名单。
[⬆ 返回总目录](#⬆ 返回总目录)
三十六、JdbcTemplate 与 Spring JDBC
36.1 为什么需要 JdbcTemplate
原生 JDBC:
java
Connection
PreparedStatement
ResultSet
try/catch/finally
close
大量样板代码。
JdbcTemplate 把固定流程封装。
36.2 查询示例
java
String sql = """
SELECT id, name, age
FROM user
WHERE id = ?
""";
User user =
jdbcTemplate.queryForObject(
sql,
(rs, rowNum) ->
new User(
rs.getLong("id"),
rs.getString("name"),
rs.getInt("age")
),
userId
);
36.3 Update 示例
java
jdbcTemplate.update(
"""
UPDATE account
SET balance = balance - ?
WHERE id = ?
""",
amount,
accountId
);
36.4 Template Method 思想
JdbcTemplate 帮你处理:
text
获取 Connection
创建 Statement
执行
遍历
异常转换
资源释放
你只提供:
text
SQL
参数
RowMapper
36.5 事务兼容
在 Spring 事务中:
text
JdbcTemplate
会通过 Spring 资源管理机制拿到:
text
当前事务绑定 Connection
而不是自己随便开一个无关连接。
36.6 适合
text
SQL 可控
简单查询
批处理
不想引完整 ORM
36.7 不适合
复杂动态 SQL:
text
大量条件拼接
对象映射复杂
可能 MyBatis 等更合适。
[⬆ 返回总目录](#⬆ 返回总目录)
三十七、数据访问异常统一转换
37.1 为什么需要
JDBC/数据库厂商异常:
text
SQLState
SQLException
Vendor Code
业务很难统一处理。
Spring 提供:
text
DataAccessException
异常体系。
37.2 好处
把不同技术异常统一成:
text
DuplicateKeyException
DataIntegrityViolationException
DeadlockLoser 等语义化异常体系
具体类会随版本演进。
37.3 @Repository
@Repository 不只是语义标记。
Spring 可以通过异常转换后置处理器:
text
把持久化技术异常
转换成 Spring DataAccessException
37.4 业务层不要到处 catch SQLException
更推荐:
text
数据访问层异常抽象
↓
Service 根据业务需要转换
↓
BizException
[⬆ 返回总目录](#⬆ 返回总目录)
三十八、REST Client、WebClient 与 HTTP 调用
现代 Spring 同时提供多种 HTTP 客户端能力。
38.1 RestClient
适合:
text
同步阻塞式 HTTP 调用
提供现代 fluent API。
示例概念:
java
RestClient client =
RestClient.create(
"https://api.example.com");
UserVO user =
client.get()
.uri("/users/{id}", id)
.retrieve()
.body(UserVO.class);
38.2 WebClient
来自 WebFlux 模块。
适合:
text
响应式/非阻塞 HTTP 客户端
38.3 什么时候用 RestClient
如果系统是:
text
传统 Spring MVC
同步业务
每次 RPC 阻塞等待
RestClient 通常更直观。
38.4 什么时候用 WebClient
如果:
text
本身使用 Reactive 链路
高并发 IO
需要流式数据
WebClient 更合适。
38.5 不要在 MVC 中盲目 WebClient.block()
如果:
text
最终每次都 block()
很多响应式优势会消失。
不是说不能用,而是:
text
架构收益要真实存在
38.6 生产 HTTP Client 必须考虑
text
Connect Timeout
Read/Response Timeout
连接池
DNS
重试
幂等性
熔断
限流
大 Body
日志脱敏
38.7 为什么重试很危险
支付:
text
POST /pay
超时了。
你不知道:
text
服务端到底执行没执行
直接自动重试可能:
text
重复支付
所以重试必须结合:
text
幂等 Key
接口语义
错误类型
[⬆ 返回总目录](#⬆ 返回总目录)
三十九、异步任务、定时任务与线程池
39.1 @Async
Spring 提供:
java
@Async
基于代理把调用提交到:
text
TaskExecutor
39.2 底层
text
Proxy
↓
Async Interceptor
↓
TaskExecutor
↓
Worker Thread
↓
Target
39.3 常见坑:自调用
java
this.asyncMethod();
可能绕过代理。
39.4 异步异常
返回 void 的异步方法:
text
异常不能像同步调用一样直接抛回调用线程
需要:
text
AsyncUncaughtExceptionHandler
日志
监控
等处理。
39.5 强可靠任务不要只用 @Async
应用宕机:
text
内存任务可能丢
强可靠通知:
text
MQ
通常更适合。
39.6 @Scheduled
java
@Scheduled(
cron = "0 */5 * * * ?"
)
public void refresh() {
}
39.7 多实例重复执行
应用 5 个实例:
text
通常 5 个都会跑定时任务
如果要求全局一次:
text
分布式锁
调度中心
Leader
39.8 线程池生产原则
明确:
text
corePoolSize
maxPoolSize
queueCapacity
keepAlive
rejection policy
thread name
metrics
不要:
text
无界队列 + 无监控
[⬆ 返回总目录](#⬆ 返回总目录)
四十、Spring Cache
40.1 是什么
Spring Cache 是:
text
缓存抽象
不是 Redis 本身。
40.2 @Cacheable
java
@Cacheable(
cacheNames = "product",
key = "#id"
)
public ProductVO get(Long id) {
return repository.query(id);
}
流程:
text
Proxy
↓
CacheInterceptor
↓
计算 Key
↓
查 Cache
├─ 命中 → 返回
└─ 未命中 → 执行方法 → 写 Cache
40.3 @CachePut
text
执行方法
并更新缓存
40.4 @CacheEvict
删除缓存。
40.5 适合
text
字典
配置
商品详情
低更新频率查询
简单方法结果缓存
40.6 不适合只靠 Spring Cache 解决
text
热点 Key
缓存击穿
大 Key
复杂批量缓存
逻辑过期
强一致缓存
这些要深入到具体缓存系统设计。
40.7 自调用同样有代理问题
text
@Cacheable
@Transactional
@Async
都有共同根因:
text
基于代理
这就是为什么学好 AOP 后很多知识会串起来。
[⬆ 返回总目录](#⬆ 返回总目录)
四十一、Spring Test 测试体系
Spring Framework 自带成熟测试支持。
不要把"测试"理解成:
text
只要能跑 JUnit 就行
真正需要区分:
text
纯单元测试
Spring Context 集成测试
MVC 测试
事务测试
Web 测试
41.1 纯单元测试
例如:
java
class PriceServiceTest {
@Test
void should_calculate_discount() {
PriceService service =
new PriceService();
BigDecimal result =
service.calculate(
new BigDecimal("100"));
assertEquals(
new BigDecimal("90"),
result
);
}
}
不启动 Spring。
优点:
text
快
稳定
定位问题直接
适合:
text
算法
业务规则
领域服务
工具类
41.2 Spring TestContext Framework
当你需要:
text
真实 Bean 注入
@Configuration
@Transactional
数据库
Spring 生命周期
可以启动 Spring Test Context。
它会帮助:
text
创建 Context
缓存 Context
注入测试实例
管理事务
41.3 Context 缓存
测试框架会尽量复用:
text
配置相同的 ApplicationContext
减少重复启动成本。
如果测试类不断使用不同:
text
配置
Profile
Mock
Property
会导致:
text
Context Cache Miss
测试变慢。
41.4 @DirtiesContext
表示:
text
当前测试污染了 Context
后面别复用了
非常有用,但滥用会导致:
text
大量 Context 重建
测试变慢
41.5 事务测试
测试方法可以:
java
@Transactional
@Test
void testCreateOrder() {
}
很多 Spring Test 场景会:
text
测试结束自动回滚
非常方便数据库测试。
但是要注意:
text
测试里的事务边界
和真实 HTTP 请求事务边界
可能不一样
41.6 常见"测试通过,线上失败"
例如测试:
text
整个测试方法一个事务
ORM 懒加载始终可用。
真实生产:
text
Service 事务结束
Controller 序列化才访问懒加载属性
结果:
text
线上 LazyInitializationException
所以测试要尽量模拟真实边界。
41.7 MockMvc
Spring MVC 测试可以:
text
不真的打开网络端口
直接模拟 MVC 请求链。
示例:
java
mockMvc.perform(
get("/users/1"))
.andExpect(
status().isOk());
适合测试:
text
URL Mapping
参数
校验
JSON
异常处理
Interceptor
41.8 WebTestClient
既可用于响应式 Web 测试,也能在某些 Spring Web 测试场景中提供 fluent API。
41.9 测试原则
推荐:
text
大量纯单元测试
适量 Spring 集成测试
针对 Web 使用 MockMvc/WebTestClient
少量真正端到端测试
不要所有测试都启动完整系统。
[⬆ 返回总目录](#⬆ 返回总目录)
四十二、AOT 与现代 Spring
42.1 AOT 是什么
AOT:
text
Ahead-of-Time
提前处理
Spring 传统运行时会:
text
扫描
反射
解析 BeanDefinition
动态代理
AOT 希望把部分工作:
text
提前到构建阶段
42.2 为什么需要
主要目标包括:
text
更快启动
减少运行时动态发现
支持 GraalVM Native Image
42.3 AOT 会带来什么限制
当很多决策提前:
text
Classpath 更固定
Bean 模型更固定
运行时动态改变能力减少
例如:
text
动态注册未知 Bean
运行时任意反射
需要更明确的提示信息。
42.4 Runtime Hints
Native Image 构建需要知道:
text
哪些类需要反射
哪些资源需要打包
哪些代理需要生成
哪些序列化类型需要保留
Spring 提供:
text
RuntimeHints
体系支持。
42.5 什么时候值得关注
如果你开发:
text
Spring Boot Native
Serverless
极快扩缩容
短生命周期程序
AOT 很重要。
普通长期运行企业后端:
text
先把核心 Spring 原理学好
优先级更高。
[⬆ 返回总目录](#⬆ 返回总目录)
四十三、Spring 中常见设计模式
Spring 是学习设计模式非常好的源码项目。
43.1 工厂模式
典型:
text
BeanFactory
FactoryBean
BeanFactory:
text
管理对象创建
FactoryBean:
text
封装复杂对象生产
43.2 单例模式思想
Spring singleton:
text
容器级单例
由容器维护:
text
singletonObjects
43.3 代理模式
最典型:
text
AOP
@Transactional
@Async
@Cacheable
43.4 模板方法模式
例如:
text
JdbcTemplate
AbstractApplicationContext
父类固定整体流程:
text
公共步骤
子类只扩展:
text
特定步骤
43.5 适配器模式
Spring MVC:
text
HandlerAdapter
DispatcherServlet 不关心不同 Handler 的具体调用方式。
43.6 观察者模式
Spring Event:
text
Publisher
Listener
Multicaster
43.7 责任链模式
AOP:
text
Interceptor Chain
Servlet:
text
Filter Chain
43.8 策略模式
Spring 内部大量:
text
接口 + 多实现
例如:
text
HandlerMapping
HandlerAdapter
TransactionManager
Resource
运行时选择合适实现。
43.9 面试不要只报模式名
好的回答:
Spring 的代理模式最典型用于 AOP 和声明式事务;模板方法可以看 JdbcTemplate 和 ApplicationContext refresh 体系;适配器模式体现在 Spring MVC 的 HandlerAdapter;观察者模式体现在 ApplicationEvent;责任链体现在 AOP MethodInterceptor 链和 Servlet Filter 链。
[⬆ 返回总目录](#⬆ 返回总目录)
四十四、生产环境性能问题与故障排查
真正工作中,Spring 知识最终要落到:
text
问题怎么定位
44.1 Bean 注入失败
异常:
text
NoSuchBeanDefinitionException
按顺序查:
text
1. 类是不是 Bean?
2. 包有没有扫描?
3. @Bean 方法是否执行到?
4. @Profile 是否激活?
5. @Conditional 是否满足?
6. 类型是不是写错?
7. 是否模块依赖没引入?
8. BeanDefinition 是否动态注册失败?
44.2 多 Bean 冲突
异常:
text
NoUniqueBeanDefinitionException
查:
text
@Primary
@Qualifier
Bean Name
重复 @Bean
重复组件扫描
框架自动注册
44.3 BeanCurrentlyInCreationException
通常考虑:
text
构造器循环依赖
复杂循环依赖
初始化阶段递归 getBean
BeanPostProcessor 提前创建
不要看到这个错误就机械说:
text
"三级缓存失效了"
要看实际依赖图。
44.4 Bean 创建慢
可能:
text
构造器慢
@PostConstruct 慢
BPP 慢
远程调用
数据库连接
大文件
锁
44.5 事务不生效
标准排查:
text
是不是 Spring Bean?
↓
有没有事务代理?
↓
调用有没有经过代理?
↓
方法是否适合代理?
↓
异常是否被吞?
↓
rollback rule?
↓
传播行为?
↓
是不是跨线程?
↓
是不是同一个事务资源?
44.6 接口突然很慢
按链路:
text
Filter
↓
Interceptor
↓
Controller
↓
AOP
↓
Service
↓
锁
↓
DB Pool
↓
SQL
↓
Redis
↓
RPC
↓
序列化
Spring 只是链路的一部分。
44.7 CPU 高
Spring 相关可能:
text
AOP 高频复杂表达式
序列化大对象
大量 SpEL
死循环 Bean
错误重试
日志风暴
但要用:
text
Profiler
JFR
Thread Dump
确认,不要猜。
44.8 内存上涨
检查:
text
单例 Bean 持有大集合
无界本地缓存
ThreadLocal
线程池队列
ApplicationListener 持有对象
动态代理/类加载
缓存没有淘汰
44.9 ThreadLocal 泄漏
Web 容器线程:
text
会被线程池复用
如果:
java
threadLocal.set(user);
没有:
java
remove();
下一个请求可能:
text
读到旧数据
还可能造成内存引用无法及时释放。
推荐:
java
try {
threadLocal.set(ctx);
chain.doFilter(...);
} finally {
threadLocal.remove();
}
44.10 日志/审计 AOP 太重
如果一个切面:
text
每个 Service 方法
同步写数据库
同步调远程审计
会给所有接口增加固定延迟。
横切逻辑的特点是:
text
一旦慢
影响面非常大
所以切面要轻量、有监控。
44.11 代理层级过多
一个 Bean 可能被:
text
事务
缓存
异步
权限
审计
监控
多个 Advisor 增强。
这不一定是问题,但如果调用链异常复杂:
text
调试和异常顺序会越来越难
不要把所有业务需求都切面化。
44.12 Full GC 与 Spring
Spring 本身通常不是:
text
Full GC 的直接原因
但容器单例持有对象生命周期长。
如果 singleton:
text
Map 永不删除
List 无限增长
缓存无上限
这些对象自然长期存活。
[⬆ 返回总目录](#⬆ 返回总目录)
四十五、Spring 源码阅读路线
源码学习一定要:
text
带问题看源码
不要一上来打开:
text
spring-context
从第一行读到最后一行。
45.1 第一阶段:IoC 基本接口
先认识:
text
BeanFactory
ListableBeanFactory
ConfigurableBeanFactory
ApplicationContext
BeanDefinition
BeanDefinitionRegistry
问题:
text
Bean 怎么描述?
Bean 怎么存?
Bean 怎么取?
45.2 第二阶段:refresh()
入口:
text
AbstractApplicationContext#refresh
目标:
text
把容器启动十几个步骤讲顺
重点:
text
invokeBeanFactoryPostProcessors
registerBeanPostProcessors
finishBeanFactoryInitialization
45.3 第三阶段:Bean 创建
核心:
text
AbstractBeanFactory#doGetBean
AbstractAutowireCapableBeanFactory#createBean
AbstractAutowireCapableBeanFactory#doCreateBean
createBeanInstance
populateBean
initializeBean
目标:
text
一个 Bean 从 BeanDefinition 到对象的完整流程
45.4 第四阶段:Autowired
看:
text
AutowiredAnnotationBeanPostProcessor
DependencyDescriptor
DefaultListableBeanFactory#doResolveDependency
findAutowireCandidates
目标:
text
为什么一个类型多个 Bean 会冲突
@Primary/@Qualifier 如何参与
45.5 第五阶段:循环依赖
核心类:
text
DefaultSingletonBeanRegistry
核心结构:
text
singletonObjects
earlySingletonObjects
singletonFactories
结合:
text
doCreateBean
addSingletonFactory
getSingleton
getEarlyBeanReference
理解。
45.6 第六阶段:AOP
看:
text
AbstractAutoProxyCreator
ProxyFactory
AopProxy
Advisor
MethodInterceptor
ReflectiveMethodInvocation
目标:
text
代理何时创建
Advisor 怎么匹配
拦截器链怎么执行
45.7 第七阶段:事务
看:
text
TransactionInterceptor
TransactionAspectSupport
PlatformTransactionManager
TransactionSynchronizationManager
目标:
text
@Transactional 如何从注解变成 commit/rollback
45.8 第八阶段:Spring MVC
看:
text
DispatcherServlet
RequestMappingHandlerMapping
RequestMappingHandlerAdapter
HandlerMethodArgumentResolver
HandlerMethodReturnValueHandler
HttpMessageConverter
HandlerExceptionResolver
目标:
text
HTTP 请求怎样变成 Controller 方法调用
45.9 第九阶段:数据访问
看:
text
JdbcTemplate
DataSourceUtils
SQLExceptionTranslator
理解:
text
Connection 怎么参与事务
异常怎么转换
45.10 源码阅读技巧
每次只回答一个问题。
例如:
text
@Autowired 字段到底在哪赋值?
就沿:
text
populateBean
↓
AutowiredAnnotationBeanPostProcessor
看。
不要突然跳去:
text
事务
MVC
AOT
[⬆ 返回总目录](#⬆ 返回总目录)
四十六、高频面试题与连续追问
下面按照真正面试"追问链"组织。
46.1 Spring 核心是什么?
第一层:
text
IoC + AOP
追问:
IoC 到底反转什么?
text
对象创建权
依赖管理权
生命周期管理权
追问:
DI 和 IoC 区别?
text
IoC 是思想
DI 是主要实现方式
46.2 @Service 怎么变成 Bean?
text
@ComponentScan
↓
Scanner
↓
读取 class 元数据
↓
生成 BeanDefinition
↓
注册 BeanDefinitionRegistry
↓
后续创建 Bean
追问:
扫描后马上 new 吗?
text
不是
先注册 BeanDefinition
46.3 refresh 做什么?
不要背所有函数,先说:
text
准备 BeanFactory
执行 BeanFactoryPostProcessor
注册 BeanPostProcessor
初始化事件等基础设施
创建非懒加载单例
完成刷新
46.4 Bean 生命周期?
text
实例化
依赖注入
Aware
Before BPP
初始化回调
After BPP
代理
使用
销毁
追问:
AOP 大概什么时候产生?
通常和:
text
BeanPostProcessor 初始化后处理
密切相关。
46.5 BeanFactoryPostProcessor 和 BeanPostProcessor?
text
前者主要处理 BeanDefinition
后者处理 Bean 实例
46.6 FactoryBean 和 BeanFactory?
text
BeanFactory = 容器
FactoryBean = 生产特殊对象的 Bean
46.7 Spring 如何处理循环依赖?
text
单例 + 可提前暴露场景
使用经典三级缓存
追问:
三个缓存?
text
singletonObjects
earlySingletonObjects
singletonFactories
追问:
为什么三级?
text
延迟生成 early reference
兼容 AOP 代理
追问:
构造器为什么难解决?
text
对象还没实例化
没有半成品可以暴露
46.8 AOP 底层?
text
代理 + Advisor + MethodInterceptor Chain
追问:
Spring AOP Join Point 是什么?
核心是:
text
方法执行
追问:
为什么 self-invocation 失效?
text
this 调用没有重新经过 proxy
46.9 JDK 和 CGLIB?
text
JDK 主要基于接口
CGLIB 基于子类
追问:
final 为什么有问题?
text
子类不能继承/覆盖
46.10 @Transactional 原理?
text
Transaction Advisor
↓
TransactionInterceptor
↓
TransactionManager
↓
目标方法
↓
commit/rollback
追问:
Connection 怎么共享?
text
TransactionSynchronizationManager
线程绑定资源
追问:
新线程呢?
text
不会天然继承
46.11 REQUIRED 和 REQUIRES_NEW?
REQUIRED:
text
有加入、无新建
REQUIRES_NEW:
text
独立新事务
外层挂起
追问:
REQUIRES_NEW 什么生产坑?
text
额外占连接
高并发可能耗尽连接池
46.12 NESTED 和 REQUIRES_NEW?
text
NESTED 常见基于 Savepoint
REQUIRES_NEW 是独立事务
46.13 为什么 catch 异常事务可能不回滚?
因为事务拦截器看到:
text
目标方法正常返回
46.14 UnexpectedRollbackException 为什么出现?
text
内层把共享事务标 rollback-only
外层吞异常后仍尝试提交
最终发现只能回滚
46.15 Spring MVC 流程?
text
DispatcherServlet
↓
HandlerMapping
↓
HandlerAdapter
↓
ArgumentResolver
↓
Controller
↓
ReturnValueHandler
↓
HttpMessageConverter
46.16 Filter、Interceptor、AOP 区别?
text
Filter:Servlet
Interceptor:Spring MVC
AOP:Spring Bean Method
46.17 @RequestBody 怎么变 Java 对象?
text
ArgumentResolver
↓
HttpMessageConverter
↓
JSON 反序列化
46.18 Spring Event 是 MQ 吗?
不是。
text
默认是进程内事件机制
46.19 Spring 单例 Bean 线程安全吗?
text
不保证
无状态 Bean 通常可安全并发使用。
46.20 @Async、@Cacheable、@Transactional 为什么都有自调用问题?
因为共同核心:
text
Spring AOP Proxy
这是一个很好的"知识串联题"。
[⬆ 返回总目录](#⬆ 返回总目录)
四十七、一张主线串起整个 Spring
最终你要形成下面这一条完整链路。
text
Spring Framework
│
┌─────────────┴─────────────┐
│ │
IoC AOP
│ │
↓ ↓
BeanDefinition Advisor
│ ┌──────┴──────┐
↓ │ │
BeanDefinitionRegistry Pointcut Advice
│ │
↓ ↓
ApplicationContext MethodInterceptor
│ │
↓ ↓
refresh() Proxy
│ │
┌──────┼─────────┐ │
│ │ │ │
BFPP BPP Event/Resource │
│ │ │
│ └──────────────────┐ │
│ │ │
↓ ↓ │
BeanDefinition Bean Creation │
│ │
├─ instantiate │
├─ populateBean │
├─ @Autowired │
├─ Aware │
├─ init │
└─ BPP after ───────┘
│
↓
Proxy Bean
│
┌────────────┼─────────────┐
│ │ │
Transaction Cache Async
│
↓
TransactionInterceptor
│
↓
TransactionManager
│
↓
TransactionSynchronizationManager
│
↓
Connection
│
↓
Database
循环依赖:
Bean A
│
↓
实例化 A
│
↓
三级缓存 ObjectFactory
│
↓
创建 B
│
↓
B 需要 A
│
↓
getEarlyBeanReference(A)
│
↓
二级缓存
│
↓
B 完成
│
↓
A 完成
│
↓
一级缓存
Spring MVC:
HTTP
↓
Servlet Container
↓
Filter
↓
DispatcherServlet
↓
HandlerMapping
↓
Interceptor
↓
HandlerAdapter
↓
ArgumentResolver
↓
Controller
↓
Service Proxy
↓
Transaction / AOP
↓
Repository / JdbcTemplate
↓
DB / Redis / RPC
↓
ReturnValueHandler
↓
HttpMessageConverter
↓
JSON Response
最终学习标准
学完这份 Spring 专题后,你不应该只会说:
text
Spring 有 IoC 和 AOP
Spring 用三级缓存
Spring 事务基于 AOP
而应该能完整解释:
text
一个 @Service 是怎样从 class 文件变成 BeanDefinition
↓
BeanDefinition 怎样注册进 BeanFactory
↓
refresh() 做了什么
↓
Bean 怎样实例化
↓
@Autowired 怎样解析候选
↓
BeanPostProcessor 怎样参与生命周期
↓
为什么循环依赖要提前暴露
↓
为什么三级缓存保存 ObjectFactory
↓
AOP 代理什么时候生成
↓
代理调用怎样执行拦截器链
↓
@Transactional 怎样通过 TransactionInterceptor 工作
↓
Connection 为什么能在线程中共享
↓
为什么 self-invocation 和多线程会导致事务问题
↓
HTTP 请求怎样经过 DispatcherServlet 调到 Controller
↓
@RequestBody 怎样反序列化
↓
异常怎样被统一处理
↓
最终怎样返回 JSON
能把这条链脱稿讲清楚,Spring 就真正从"八股知识"变成了你的工程知识。
面试前 80 道自测题
- Spring Framework 是什么?
- 为什么需要 IoC?
- IoC 到底反转什么?
- IoC 和 DI 有什么区别?
- 为什么推荐构造器注入?
- 字段注入有什么问题?
- BeanDefinition 是什么?
- BeanDefinition 与 Bean 有什么区别?
@Service如何被发现?@ComponentScan的底层思路是什么?@Bean和@Component有什么区别?@Import有哪些高级用法?- ImportSelector 做什么?
- ImportBeanDefinitionRegistrar 做什么?
- BeanFactory 和 ApplicationContext 区别?
refresh()为什么是 Spring 核心?- refresh 大致有哪些阶段?
- BeanFactoryPostProcessor 什么时候执行?
- BeanPostProcessor 什么时候注册?
ConfigurationClassPostProcessor作用是什么?- 一个 Bean 从
getBean()到创建完成经历什么? doCreateBean()主要做什么?populateBean()做什么?initializeBean()做什么?@Autowired谁处理?- 多个同类型 Bean 如何选?
@Primary和@Qualifier区别?- List/Map 注入有什么实际用途?
- ObjectProvider 适合什么场景?
- Bean 生命周期完整顺序是什么?
- Aware 有哪些典型接口?
- 为什么普通业务代码少用 ApplicationContextAware?
@PostConstruct适合做什么?@PostConstruct做远程 IO 有什么风险?- FactoryBean 与 BeanFactory 有什么区别?
- MyBatis Mapper 没实现类为什么能注入?
- singleton Bean 是否线程安全?
- 为什么无状态 Service 通常安全?
- prototype 注入 singleton 有什么坑?
- 什么是循环依赖?
- 三级缓存分别是什么?
- 为什么需要二级缓存?
- 为什么需要三级缓存?
getEarlyBeanReference()做什么?- 构造器循环依赖为什么难解决?
- prototype 循环依赖为什么不同?
- 什么是 Aspect?
- 什么是 Pointcut?
- 什么是 Advice?
- Advisor 是什么?
- Spring AOP 的 Join Point 是什么?
- AOP 代理大概什么时候创建?
- AbstractAutoProxyCreator 做什么?
- JDK 动态代理原理是什么?
- CGLIB 原理是什么?
- final 为什么影响 CGLIB?
- MethodInterceptor Chain 怎么工作?
- 为什么 AOP self-invocation 会失效?
@Transactional底层原理是什么?- TransactionInterceptor 做什么?
- TransactionManager 做什么?
- TransactionSynchronizationManager 做什么?
- 为什么同一个事务能共享 Connection?
- 为什么跨线程事务不自动传播?
- REQUIRED 与 REQUIRES_NEW 区别?
- NESTED 与 REQUIRES_NEW 区别?
- UnexpectedRollbackException 如何产生?
- 默认什么异常会回滚?
- readOnly 是不是绝对禁止 UPDATE?
- 事务里调用慢 RPC 有什么问题?
- Spring MVC 请求完整链路是什么?
- DispatcherServlet 做什么?
- HandlerMapping 做什么?
- HandlerAdapter 为什么存在?
- ArgumentResolver 做什么?
- HttpMessageConverter 做什么?
- Filter、Interceptor、AOP 怎么选?
- Spring Event 能否替代 MQ?
- JdbcTemplate 为什么能参与 Spring 事务?
- 如果线上 Spring 接口突然变慢,你怎么按层排查?
推荐复习顺序
如果目标是 Java 后端面试:
第一优先级
text
IoC / DI
BeanDefinition
refresh()
Bean 生命周期
@Autowired
BeanPostProcessor
循环依赖
三级缓存
第二优先级
text
AOP
JDK / CGLIB
Advisor / Interceptor Chain
@Transactional
事务传播
事务失效
第三优先级
text
Spring MVC
参数解析
HttpMessageConverter
异常处理
Filter / Interceptor
第四优先级
text
Event
Resource
ConversionService
SpEL
JdbcTemplate
REST Client
Async
Scheduled
Cache
Test
AOT
官方参考资料
-
Spring Framework Reference
-
Core Technologies
-
IoC Container
https://docs.spring.io/spring-framework/reference/core/beans.html
-
Spring AOP
https://docs.spring.io/spring-framework/reference/core/aop.html
-
Transaction Management
https://docs.spring.io/spring-framework/reference/data-access/transaction.html
-
Spring MVC
https://docs.spring.io/spring-framework/reference/web/webmvc.html
-
Spring WebFlux
https://docs.spring.io/spring-framework/reference/web/webflux.html
-
Spring JDBC
https://docs.spring.io/spring-framework/reference/data-access/jdbc.html
-
Spring Testing
https://docs.spring.io/spring-framework/reference/testing.html
-
AOT
https://docs.spring.io/spring-framework/reference/core/aot.html
建议的源码实战方式:
不要一次阅读整个 Spring 源码。可以自己写一个只有 5 个 Bean 的 Demo,然后分别在:
refresh()→doGetBean()→doCreateBean()→populateBean()→AutowiredAnnotationBeanPostProcessor→DefaultSingletonBeanRegistry#getSingleton()→AbstractAutoProxyCreator→TransactionInterceptor→DispatcherServlet上打断点。每次只追踪一个问题。这样比看几十篇零散源码博客更容易形成自己的 Spring 主线。
