一、Spring 核心架构
- 核心容器:Bean、Core、Context、SpEL
- 核心能力:IOC 控制反转、DI 依赖注入、AOP 面向切面编程
- 作用:解耦、统一对象生命周期、统一事务管理、简化开发
二、IOC 控制反转(最核心)
1. 什么是 IOC
- 把对象创建、依赖管理、生命周期交给 Spring 容器管理
- 程序员不再 new 对象,由容器自动注入
- 目的:解耦、便于统一管理、便于扩展、便于测试
2. IOC 容器顶层接口
- BeanFactory:基础容器,延迟初始化(获取 Bean 时才创建)
- ApplicationContext:高级容器(BeanFactory 子类),项目常用,启动时初始化所有单例 Bean,提供国际化、事件发布等扩展能力
3. Bean 生命周期(面试必背)
- 实例化(调用构造方法创建 Bean 实例)
- 填充属性(DI 依赖注入,给 @Autowired / @Resource 字段赋值)
- 执行 Aware 接口方法(BeanNameAware 获取 Bean 名称、BeanFactoryAware 获取容器、ApplicationContextAware 获取上下文)
- 执行 BeanPostProcessor 前置处理(postProcessBeforeInitialization,所有 Bean 初始化前统一增强)
- 执行初始化方法(先 @PostConstruct 注解方法 → 再 afterPropertiesSet 接口方法 → 最后 xml 配置的 init-method)
- 执行 BeanPostProcessor 后置处理(postProcessAfterInitialization,所有 Bean 初始化后统一增强,AOP 代理生成在此阶段)
- Bean 初始化完成,进入容器缓存,可被使用
- 销毁阶段(容器关闭时:先 @PreDestroy 注解方法 → 再 destroy 接口方法 → 最后 xml 配置的 destroy-method)
一句话总结:实例化 → 依赖注入 → Aware 回调 → 前置处理 → 初始化 → 后置处理 → 运行 → 销毁
4. Bean 作用域
- singleton:单例(默认),整个 Spring 容器中只有一个实例,启动时创建(ApplicationContext)
- prototype:多例,每次 getBean () 都创建新对象,容器只负责创建,不管理销毁
- request:Web 环境,每个 HTTP 请求创建一个实例
- session:Web 环境,每个 HTTP Session 一个实例
- application:Web 环境,整个 Web 应用一个实例
5. Bean 循环依赖(面试核心)
什么是循环依赖
两个或多个 Bean 互相依赖:A → B → A,或 A → B → C → A
Spring 解决循环依赖的前提
- 仅支持 单例 Bean(多例 Bean 每次获取都新建,无缓存,无法解决)
- 仅支持 字段注入 / Setter 注入(构造器注入在实例化阶段就需要依赖,无法提前暴露)
- 不支持 AOP 代理的构造器注入循环依赖
三级缓存解决循环依赖的核心逻辑(超详细)
Spring 容器内部维护三个缓存(本质是 Map),核心是「提前暴露半成品 Bean」:
| 缓存级别 | 名称(源码变量) | 存储内容 | 核心作用 |
|---|---|---|---|
| 一级缓存 | singletonObjects | 完全初始化完成的单例 Bean(成品) | 供外部获取最终可用的 Bean |
| 二级缓存 | earlySingletonObjects | 早期暴露的半成品 Bean(已实例化、未填充属性 / 未执行初始化) | 避免重复创建代理,缓存提前暴露的 Bean |
| 三级缓存 | singletonFactories | ObjectFactory 工厂对象(创建 Bean 实例 / 生成代理的工厂) | 延迟创建代理,解决「实例化」和「代理生成」的时序问题 |
完整解决流程(以 A → B → A 为例)
-
容器创建 Bean A:
- 执行构造方法,完成实例化(此时仅创建空对象,无属性)
- 将 A 的 ObjectFactory 放入三级缓存
- 开始填充属性,发现依赖 Bean B,暂停 A 的创建,去创建 B
-
容器创建 Bean B:
- 执行构造方法,完成实例化
- 将 B 的 ObjectFactory 放入三级缓存
- 开始填充属性,发现依赖 Bean A
- 从一级缓存找 A → 无;从二级缓存找 A → 无;从三级缓存取 A 的 ObjectFactory → 执行工厂获取 A 的半成品实例
- 将 A 的半成品实例放入二级缓存,删除三级缓存中的 A 工厂
- 把 A 的半成品实例注入到 B 中,B 继续完成属性填充、初始化,成为成品
- B 成品放入一级缓存,删除二级缓存中的 B(如有)
-
回到 Bean A 的创建流程:
- 从一级缓存获取 B 的成品,注入到 A 中
- A 继续完成属性填充、初始化、后置处理器(如生成 AOP 代理)
- A 成品放入一级缓存,删除二级缓存中的 A
- 最终一级缓存中保存 A、B 的成品 Bean,供外部使用
为什么需要三级缓存(而非两级)
- 核心目的:延迟生成 AOP 代理,避免不必要的代理创建
- 如果只有两级缓存:需要在实例化后立即生成代理并放入二级缓存,无论该 Bean 是否需要被循环依赖,都会提前生成代理,增加性能开销
- 三级缓存通过 ObjectFactory 延迟执行代理创建逻辑:只有发生循环依赖时,才会执行工厂生成代理;无循环依赖时,代理在 Bean 初始化后的后置处理器阶段生成,符合正常流程
无法解决的循环依赖场景
- 构造器注入循环依赖(实例化阶段就需要依赖,无法提前暴露半成品)
- 多例 Bean 循环依赖(多例无缓存,每次 getBean () 都新建,无法复用)
- 单例 Bean + @Scope (proxyMode = ScopedProxyMode.TARGET_CLASS) 且构造器注入
三、DI 依赖注入
1. 注入方式(优先级 / 适用场景)
- 构造器注入(Spring 4.x+ 推荐):优点:依赖明确、不可变(构造后字段无法修改)、天然避免循环依赖(编译期报错)适用:核心、必选依赖
- Setter 注入:优点:支持可选依赖、可循环依赖适用:非核心、可选依赖
- Field 注入(@Autowired):优点:代码简洁、开发效率高(最常用)缺点:依赖隐藏、无法通过构造器保证依赖非空、单元测试需手动注入
2. @Autowired 装配规则
- 按类型(byType)匹配容器中的 Bean
- 匹配到多个 → 按名称(byName)匹配字段名 / 方法参数名
- 匹配不到 → 抛 NoSuchBeanDefinitionException(required=true 时)
- 可通过 @Qualifier 指定 Bean 名称,精准匹配
3. @Resource vs @Autowired
| 特性 | @Autowired | @Resource |
|---|---|---|
| 所属规范 | Spring 自定义注解 | JSR-250 Java 标准注解 |
| 匹配规则 | 默认 byType → byName | 默认 byName → byType |
| 支持指定名称 | 需配合 @Qualifier | 直接通过 name 属性指定 |
| 支持依赖非空控制 | required 属性(默认 true) | 无,找不到则抛异常 |
| 适用场景 | 优先匹配类型,Spring 生态 | 优先匹配名称,跨框架兼容 |
四、AOP 面向切面编程
1. 核心思想
在不修改目标方法源码的前提下,通过动态代理对方法进行横向增强(如日志、事务、权限、限流、监控),实现业务逻辑与横切逻辑解耦
2. AOP 核心术语(必须吃透)
- 切面(Aspect) :封装横切逻辑的类(如 LogAspect、TransactionAspect),通常加 @Aspect 注解
- 连接点(JoinPoint) :Spring 中特指可被增强的方法执行点(如 Controller 方法、Service 方法)
- 切点(Pointcut) :匹配连接点的规则(如 execution (* com.xxx.service. . (..))),决定哪些方法被增强
- 通知(Advice) :具体的增强逻辑,分为 5 种类型
- 目标对象(Target) :被代理的原始业务对象(如 UserServiceImpl)
- 代理对象(Proxy) :Spring 生成的包含增强逻辑的对象,外部调用的是代理而非原始对象
- 织入(Weaving) :将通知应用到目标对象生成代理的过程(Spring 中织入发生在 Bean 初始化阶段)
3. 五种通知类型及执行顺序
| 通知类型 | 注解 | 执行时机 | 核心作用 |
|---|---|---|---|
| 前置通知 | @Before | 目标方法执行前 | 参数校验、日志记录(入参) |
| 环绕通知 | @Around | 目标方法执行前后(最强) | 全流程控制(限流、超时、重试) |
| 返回通知 | @AfterReturning | 目标方法正常返回后 | 结果处理、日志记录(出参) |
| 异常通知 | @AfterThrowing | 目标方法抛出异常后 | 异常捕获、告警、日志记录 |
| 最终通知 | @After | 目标方法执行完成后(无论是否异常) | 资源释放、清理操作 |
完整执行顺序(无异常):Around 前置处理 → Before → 目标方法执行 → Around 后置处理 → AfterReturning → After
有异常时顺序:Around 前置处理 → Before → 目标方法抛出异常 → Around 异常处理 → AfterThrowing → After
4. AOP 底层实现(动态代理)
JDK 动态代理
- 前提:目标对象实现至少一个接口
- 原理:通过 Proxy 类生成接口的实现类,代理类持有 InvocationHandler,invoke 方法中执行增强逻辑 + 目标方法
- 特点:基于接口,轻量,JDK 原生支持,无需额外依赖
CGLIB 动态代理
- 前提:目标对象无实现接口(或强制指定)
- 原理:通过字节码生成目标对象的子类,重写目标方法,子类中执行增强逻辑 + 父类方法
- 特点:基于子类,需依赖 cglib 包(SpringBoot 已内置),可代理任意类,无法代理 final 方法 / 类
Spring 代理选择规则(SpringBoot 2.0+)
- 目标对象有接口 → 默认用 JDK 动态代理(可通过 spring.aop.proxy-target-class=true 强制 CGLIB)
- 目标对象无接口 → 用 CGLIB 代理
- 标注 @EnableAspectJAutoProxy (proxyTargetClass = true) → 全局强制 CGLIB
五、Spring 事务核心
1. 事务管理核心组件
-
PlatformTransactionManager:事务管理器(核心),不同数据源对应不同实现:
- DataSourceTransactionManager:JDBC/MyBatis 事务
- JpaTransactionManager:JPA 事务
- RedisTransactionManager:Redis 事务(非关系型)
-
TransactionDefinition:事务定义,包含 4 大属性:
- 传播行为:事务如何嵌套
- 隔离级别:解决并发问题
- 超时时间:事务最长执行时间
- 是否只读:优化数据库执行(只读事务不写数据)
-
TransactionStatus:事务运行状态,记录事务是否激活、是否回滚、是否新建等
2. @Transactional 生效条件(缺一不可)
- 注解标注在 public 方法上(Spring 代理仅拦截 public 方法)
- 目标类被 Spring 容器管理(加 @Component/@Service 等)
- 开启事务注解驱动(SpringBoot 自动开启,纯 Spring 需加 @EnableTransactionManagement)
- 异常未被手动捕获(需抛出到事务切面,否则无法触发回滚)
- 异常类型符合回滚规则(默认回滚 RuntimeException/Error,非检查异常需指定 rollbackFor)
3. 事务传播机制(7 种,重点背 3 个)
| 传播行为 | 注解值 | 核心逻辑 | 典型场景 |
|---|---|---|---|
| REQUIRED(默认) | REQUIRED | 有事务则加入,无则新建 | 普通业务方法(大部分场景) |
| REQUIRES_NEW | REQUIRES_NEW | 强制新建事务,挂起当前事务 | 日志记录、消息发送(不影响主事务) |
| NESTED | NESTED | 嵌套事务,基于保存点回滚 | 批量操作(失败仅回滚当前批次) |
| SUPPORTS | SUPPORTS | 有事务则加入,无则无事务 | 查询方法(可选事务) |
| NOT_SUPPORTED | NOT_SUPPORTED | 无事务,挂起当前事务 | 耗时非事务操作(避免锁占用) |
| MANDATORY | MANDATORY | 必须在事务中执行,无则抛异常 | 核心写操作(强制事务) |
| NEVER | NEVER | 必须无事务,有则抛异常 | 纯查询(禁止事务) |
4. 事务隔离级别(解决并发问题)
| 隔离级别 | 注解值 | 解决的问题 | 存在的问题 | 数据库默认 |
|---|---|---|---|---|
| READ_UNCOMMITTED | 读未提交 | 无(最低级别) | 脏读、不可重复读、幻读 | 无主流数据库默认 |
| READ_COMMITTED | 读已提交 | 脏读 | 不可重复读、幻读 | Oracle、SQL Server、MySQL 8.0(部分) |
| REPEATABLE_READ | 可重复读 | 脏读、不可重复读 | 幻读(InnoDB 已解决) | MySQL(InnoDB)默认 |
| SERIALIZABLE | 串行化 | 所有问题(最高级别) | 性能极低(表级锁) | 无主流数据库默认 |
并发问题说明
- 脏读:读取到其他事务未提交的数据(如 A 改了数据未提交,B 读到了 A 的修改)
- 不可重复读:同一事务内多次读取同一数据,结果不一致(如 A 读数据后,B 修改提交,A 再读不同)
- 幻读:同一事务内多次查询同一条件,结果条数不一致(如 A 查所有用户,B 新增用户提交,A 再查多了一条)
5. 事务失效场景(高频面试)
- 非 public 方法:Spring 代理仅拦截 public 方法,private/protected 方法注解无效
- 同类内部调用:如 A.service1 () 调用 A.service2 ()(@Transactional),未走代理,失效
- 异常被吞:方法内 try-catch 异常,未抛出到切面,无法触发回滚
- 异常类型不匹配:抛出 Checked 异常(如 IOException),未指定 rollbackFor = Exception.class
- 多线程调用:主线程开启事务,子线程执行数据库操作,子线程无事务(事务不跨线程)
- 类未被 Spring 管理:未加 @Service/@Component,容器无代理对象
- 数据源未配置事务管理器:如未注入 DataSourceTransactionManager
- 只读事务写数据:@Transactional (readOnly = true) 执行 insert/update/delete,失效
六、SpringMVC 执行流程(必背)
核心执行流程(10 步详细版)
- 客户端发送 HTTP 请求 → 前端控制器 DispatcherServlet(中央入口)
- DispatcherServlet 调用 HandlerMapping(处理器映射器)→ 根据 URL 匹配对应的 Handler(Controller 方法)
- HandlerMapping 返回 HandlerExecutionChain(包含 Handler + 拦截器)给 DispatcherServlet
- DispatcherServlet 调用 HandlerAdapter(处理器适配器)→ 适配并执行 Handler
- HandlerAdapter 执行前置处理(如参数解析、@RequestParam 绑定)
- 调用 Controller 业务方法,执行核心业务逻辑
- Controller 返回 ModelAndView(数据模型 + 视图名)或 JSON 数据
- DispatcherServlet 调用 ViewResolver(视图解析器)→ 解析视图名,生成 View 对象(如 JSP/Thymeleaf)
- View 对象渲染数据(Model 填充到视图),生成响应内容(JSON 则直接返回)
- DispatcherServlet 将响应返回给客户端,完成请求处理
核心组件作用
- DispatcherServlet:核心控制器,统筹所有流程,相当于 “大脑”
- HandlerMapping:URL → Controller 方法的映射器(如 RequestMappingHandlerMapping)
- HandlerAdapter:适配不同类型的 Handler,统一执行逻辑(如 RequestMappingHandlerAdapter)
- ViewResolver:视图解析器,将逻辑视图名转为物理视图(如 InternalResourceViewResolver)
- HandlerInterceptor:拦截器,可在请求处理前后增强(如登录校验、日志)
七、SpringBoot 核心原理
1. 核心注解 @SpringBootApplication 拆解
plaintext
@SpringBootApplication =
@SpringBootConfiguration // 等价于 @Configuration,标识配置类
+ @EnableAutoConfiguration // 核心:开启自动配置
+ @ComponentScan // 扫描当前包及子包的 @Component 注解类
2. 自动配置原理(核心)
-
触发:@EnableAutoConfiguration 注解通过 @Import (AutoConfigurationImportSelector.class) 导入自动配置类
-
加载:AutoConfigurationImportSelector 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,该文件包含所有自动配置类全限定名(如 DataSourceAutoConfiguration、RedisAutoConfiguration)
-
条件生效:自动配置类通过 条件注解 控制是否生效:
- @ConditionalOnClass:类路径下存在指定类才生效(如 RedisTemplate 存在则 Redis 自动配置生效)
- @ConditionalOnBean:容器中存在指定 Bean 才生效
- @ConditionalOnMissingBean:容器中不存在指定 Bean 才生效(用户自定义优先)
- @ConditionalOnProperty:配置文件中指定属性满足条件才生效(如 spring.redis.enabled=true)
-
属性绑定:通过 @ConfigurationProperties 绑定 application.yml/properties 中的配置,覆盖默认值
3. Starter 启动器原理
-
Starter 本质:依赖聚合 + 自动配置 的组合包,无需手动导入多个依赖
-
核心结构:
- pom.xml:聚合相关依赖(如 spring-boot-starter-web 包含 tomcat、spring-web、spring-webmvc 等)
- 自动配置类:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中声明自动配置类
- 配置属性:META-INF/spring-configuration-metadata.json 定义配置项提示
-
典型示例:引入 spring-boot-starter-redis → 自动导入 Redis 依赖 + 自动配置 RedisTemplate、StringRedisTemplate
4. SpringBoot 启动流程(关键步骤)
-
执行启动类 main 方法 → 调用 SpringApplication.run ()
-
初始化 SpringApplication:设置应用类型(Web / 非 Web)、加载初始化器、监听器
-
准备环境(Environment):加载配置文件(application.yml/properties)、系统变量、命令行参数
-
创建 ApplicationContext(应用上下文):根据应用类型创建 AnnotationConfigServletWebServerApplicationContext(Web 应用)
-
刷新上下文(核心):
- 扫描 Bean 定义(@Component/@Service 等)
- 执行自动配置逻辑
- 初始化所有单例 Bean(IOC 容器初始化)
- 启动内置 Web 服务器(Tomcat/Jetty/Undertow)
-
发布启动完成事件,回调 CommandLineRunner/ApplicationRunner
-
应用启动完成,监听端口等待请求
八、第 6 天高频面试题(必背)
- Spring Bean 完整生命周期?
- 三级缓存解决循环依赖的原理?为什么需要三级缓存而非两级?
- 构造器注入、Setter 注入、Field 注入的区别及适用场景?
- AOP 五种通知的执行顺序?JDK 代理与 CGLIB 代理的区别?
- @Transactional 生效条件?常见失效场景及原因?
- 事务传播机制中 REQUIRED、REQUIRES_NEW、NESTED 的区别?
- SpringMVC 完整执行流程?核心组件的作用?
- SpringBoot 自动配置原理?Starter 是什么?
- Spring 如何解决事务的脏读、不可重复读、幻读?
- 为什么 Spring 事务不支持多线程?