Spring 工程实践与面试综合:6 期系列收官之作
本文是 "Spring 全家桶源码级深度解析" 系列的第 6 期(最终期),融合工程化规范、线上排查实战、30 道综合面试题和 EduLearn 完整复盘。
前 5 期回顾:第1期 IoC/DI | 第2期 自动配置 | 第3期 Spring MVC | 第4期 AOP 深度解析| 第5期 事务
目录
- 开篇:代码跑通了,但能上线吗?
- [一、EduLearn 工程化规范](#一、EduLearn 工程化规范)
- [1.1 分层架构:四层分离,单向依赖](#1.1 分层架构:四层分离,单向依赖)
- [1.2 统一返回体:ApiResponse](#1.2 统一返回体:ApiResponse)
- [1.3 全局异常处理:@ControllerAdvice](#1.3 全局异常处理:@ControllerAdvice)
- [1.4 参数校验:@Valid + JSR 303](#1.4 参数校验:@Valid + JSR 303)
- [1.5 日志规范](#1.5 日志规范)
- [1.6 配置管理:application.yml 环境隔离](#1.6 配置管理:application.yml 环境隔离)
- 二、线上问题排查实战
- [2.1 OOM / 内存泄漏](#2.1 OOM / 内存泄漏)
- [2.2 CPU 飙升 100%](#2.2 CPU 飙升 100%)
- [2.3 死锁](#2.3 死锁)
- [2.4 慢 SQL](#2.4 慢 SQL)
- [2.5 接口响应慢](#2.5 接口响应慢)
- [2.6 连接池耗尽](#2.6 连接池耗尽)
- [三、EduLearn 完整复盘](#三、EduLearn 完整复盘)
- [3.1 模块串联示例:用户下单全流程](#3.1 模块串联示例:用户下单全流程)
- [四、30 道 Spring 综合面试题库](#四、30 道 Spring 综合面试题库)
- [五、6 期系列总览](#五、6 期系列总览)
开篇:代码跑通了,但能上线吗?
前面 5 期,我们从 IoC 容器一路写到事务源码,用 EduLearn 在线教育平台串联了所有技术点。但写完代码只是起点------上线后才是真正的考验。
凌晨 3 点,CPU 突然飙到 100%;用户投诉下单后迟迟没反应;运维说内存一周涨了 2G......这些不是面试题,是真实的生产事故。
本期作为收官之作,不谈单个技术原理,而是把 5 期知识串成一个可落地的工程体系:分层架构怎么搭、异常怎么统一处理、问题怎么排查、面试怎么回答。
一、EduLearn 工程化规范

1.1 分层架构:四层分离,单向依赖
Controller → Service → DAO/Mapper → DB
↓ ↓ ↓
统一返回体 事务管理 MyBatis XML
参数校验 AOP横切 PageHelper
全局异常
铁律:上层依赖下层,下层绝不反向依赖上层。Service 不能引入 Controller 包,Mapper 不能引入 Service 包。
1.2 统一返回体:ApiResponse
没有统一返回体的 API 是灾难------每个接口返回格式不同,前端需要写 N 套解析逻辑。
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
private int code; // 业务状态码
private String message; // 提示信息
private T data; // 响应数据
private long timestamp; // 时间戳
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data, System.currentTimeMillis());
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null, System.currentTimeMillis());
}
}
Controller 层只返回 ApiResponse:
java
@GetMapping("/courses/{id}")
public ApiResponse<CourseVO> getCourse(@PathVariable Long id) {
return ApiResponse.success(courseService.getById(id));
}
1.3 全局异常处理:@ControllerAdvice
不要在每个 Controller 里写 try-catch------用 @ControllerAdvice 一刀切。
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<?> handleValidation(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.collect(Collectors.joining("; "));
return ApiResponse.error(400, "参数校验失败: " + msg);
}
@ExceptionHandler(BusinessException.class)
public ApiResponse<?> handleBusiness(BusinessException ex) {
return ApiResponse.error(ex.getCode(), ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ApiResponse<?> handleUnknown(Exception ex) {
log.error("未知异常", ex);
return ApiResponse.error(500, "服务器内部错误");
}
}
自定义业务异常:
java
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() { return code; }
}
这样 Service 层只需 throw new BusinessException(10001, "库存不足"),Controller 层零 try-catch。
1.4 参数校验:@Valid + JSR 303
java
@PostMapping("/orders")
public ApiResponse<OrderVO> createOrder(@Valid @RequestBody OrderDTO dto) {
return ApiResponse.success(orderService.create(dto));
}
@Data
public class OrderDTO {
@NotNull(message = "用户ID不能为空")
private Long userId;
@NotNull(message = "课程ID不能为空")
private Long courseId;
@Min(value = 1, message = "数量至少为1")
@Max(value = 10, message = "单次最多购买10门")
private int count;
}
校验失败抛出的 MethodArgumentNotValidException 由 GlobalExceptionHandler 统一捕获,返回格式化错误信息。
1.5 日志规范
yaml
logging:
level:
root: INFO
com.edulearn: DEBUG # 业务包
org.springframework.transaction: TRACE # 事务日志(排查用)
org.springframework.web: DEBUG # MVC 请求日志
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
日志使用规范:
| 级别 | 使用场景 |
|---|---|
| ERROR | 需要人工介入的异常(支付失败、数据库宕机) |
| WARN | 可自动恢复的异常(重试成功、降级处理) |
| INFO | 关键业务流程(下单、支付、退款) |
| DEBUG | 调试信息(方法入参、SQL 参数、缓存命中) |
禁止事项 :① log.info 里调用方法(如 log.info("user: {}", user.toString())------toString 在生产也会执行);② 循环内打 INFO 日志;③ e.printStackTrace()(改用 log.error("msg", e))。
java
// 正确
log.info("订单创建成功, orderId={}, userId={}", order.getId(), dto.getUserId());
// 错误
log.info("订单创建成功" + order); // 字符串拼接浪费性能
1.6 配置管理:application.yml 环境隔离
application.yml # 公共配置
application-dev.yml # 开发环境
application-test.yml # 测试环境
application-prod.yml # 生产环境
通过 spring.profiles.active=prod 切换。敏感信息(数据库密码、API Key)不要硬编码,用环境变量或配置中心:
yaml
spring:
datasource:
password: ${DB_PASSWORD} # 从环境变量读取
二、线上问题排查实战

2.1 OOM / 内存泄漏
现象 :应用运行几小时后崩溃,java.lang.OutOfMemoryError: Java heap space。
排查流程:
bash
# 1. 找到 Java 进程
jps -lv
# 2. 看堆使用情况
jmap -heap PID
# 3. 查看存活对象 Top 20
jmap -histo:live PID | head -20
# 4. 如果上面看不出,导出堆快照
jmap -dump:format=b,file=heap.hprof PID
用 Eclipse MAT(Memory Analyzer Tool)打开 heap.hprof,查看 Dominator Tree,找到占用内存最大的对象。
经典案例------ThreadLocal 内存泄漏:
java
// 问题代码
public class RequestContext {
private static ThreadLocal<User> currentUser = new ThreadLocal<>();
// 使用后没有 remove() !Tomcat 线程池复用线程 → 线程不消亡 → ThreadLocal 永远不回收
}
// 正确做法
public class RequestContext {
private static ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void clear() {
currentUser.remove(); // 在 Filter/Interceptor 的 afterCompletion 中调用
}
}
预防 :设置 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/heapdump.hprof,OOM 时自动 dump。
2.2 CPU 飙升 100%
排查流程:
bash
# 1. 找到进程内高 CPU 线程
top -Hp PID
# 2. 记录高 CPU 线程的 TID(十进制),转十六进制
printf '%x\n' TID
# 3. 用 jstack 看这个线程在干什么
jstack PID | grep -A 20 十六进制TID
Arthas 一键版(强烈推荐):
bash
# 启动 Arthas
java -jar arthas-boot.jar
# 查看最忙的 3 个线程
dashboard
# 直接找当前阻塞其他线程的元凶
thread -b
# 观察方法耗时
trace com.edulearn.service.OrderService createOrder
常见原因:
- 死循环(
while(true)没有 sleep) - 正则表达式回溯(如
(a+)+b匹配长字符串) - 频繁 Full GC(堆快满了,GC 线程持续工作 → 看 GC 日志)
2.3 死锁
现象:请求卡住不返回,线程数持续增长。
bash
# jstack 直接帮你找到死锁
jstack PID | grep "Found one Java-level deadlock" -A 50
输出会明确指出哪些线程持有哪些锁、等待哪些锁:
Found one Java-level deadlock:
==============================
"Thread-1":
waiting to lock monitor 0x00007f8a1c001a58 (object 0x000000076abcf1d0, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f8a1c003f58 (object 0x000000076abcf1e0, a java.lang.Object),
which is held by "Thread-1"
Arthas 版 :thread -b 直接输出死锁链。
根因与预防:
- 统一加锁顺序(所有地方先锁 A 再锁 B)
- 减小锁粒度(不要锁整个方法,只锁关键代码块)
- 用
ReentrantLock.tryLock(timeout, unit)替代synchronized
2.4 慢 SQL
bash
# 1. 找到正在执行的慢查询
SHOW FULL PROCESSLIST;
# 2. 启用慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 超过 1 秒记录
# 3. 分析执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';
EXPLAIN 关键字段:
| 字段 | 含义 | 理想值 |
|---|---|---|
| type | 访问类型 | const > ref > range > index > ALL |
| key | 使用的索引 | 非空 |
| rows | 扫描行数 | 越小越好 |
| Extra | 额外信息 | 避免 Using filesort / Using temporary |
常见慢 SQL 优化:
- 缺少索引 →
CREATE INDEX idx_user_status ON orders(user_id, status) SELECT *→ 只查需要的列LIMIT 100000, 20深分页 → 改用游标分页(WHERE id > lastId LIMIT 20)- JOIN 字段类型不一致(隐式转换导致全表扫描)
- 未利用覆盖索引 →
EXPLAIN看 Extra 是否是Using index
2.5 接口响应慢
先用 SkyWalking / Pinpoint 等 APM 工具定位到具体接口,再用 Arthas trace:
bash
arthas> trace com.edulearn.controller.OrderController createOrder -n 5
输出完整的调用链和每一层的耗时,一眼看出瓶颈在 Service、DAO 还是第三方调用。
2.6 连接池耗尽
bash
# 连接池配置(HikariCP 默认)
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.connection-timeout=30000
如果日志出现 Connection is not available, request timed out after 30000ms,排查:
- 是否有连接泄漏(获取连接后没关,可用
jstack查看线程是否 BLOCKED 在getConnection) - 最大连接数是否太小
- 是否有慢 SQL 长时间持有连接
三、EduLearn 完整复盘
6 期系列贯穿的 EduLearn 在线教育平台,最终架构如下:
| 模块 | 核心功能 | 用到的主要技术 |
|---|---|---|
| 用户模块 | 注册登录、JWT认证、角色权限、手机验证码 | IoC(MVC注入)、MVC(RESTful Controller)、AOP(日志切面)、Spring Security |
| 课程模块 | CRUD、ES搜索、分类标签、Redis缓存 | IoC(Bean管理)、自动配置(ES Starter)、MVC(分页查询) |
| 订单模块 | 下单、支付回调、退款、库存扣减 | 事务(核心)、AOP(日志)、MVC(RESTful) |
| 学习模块 | 课程进度、笔记、视频播放、作业提交 | MVC(文件上传)、自动配置(OSS Starter) |
| 消息模块 | 站内信、推送通知、RabbitMQ异步 | 事务(REQUIRES_NEW)、消息队列 |
技术栈全貌:
Spring Boot 2.7.x # 基础框架
├── Spring IoC/DI # 第1期:Bean生命周期、依赖注入、自动装配
├── Spring Boot 自动配置 # 第2期:Starter机制、条件注解、配置绑定
├── Spring MVC # 第3期:DispatcherServlet、拦截器、RESTful
├── Spring AOP # 第4期:动态代理、切面编程、通知顺序
├── Spring 事务 # 第5期:@Transactional、传播行为、失效场景
├── Spring Security + JWT # 认证与授权
├── MyBatis / MyBatis-Plus # 数据访问层
├── Redis + Spring Cache # 缓存
├── Elasticsearch # 全文搜索
├── RabbitMQ # 消息队列
└── Actuator + Prometheus # 监控
3.1 模块串联示例:用户下单全流程
这是整个系统最复杂的业务流程,串联了 5 个模块的技术点:
1. [用户模块] JWT 解析用户身份 → SecurityContext
2. [Controller] @Valid 校验 OrderDTO 参数 → 统一异常处理兜底
3. [AOP] @LogAspect 记录接口调用日志
4. [Service - 事务] @Transactional 开启事务
├── [课程模块] 查询课程信息 + Redis 缓存预热
├── [订单模块] 创建订单记录 → MyBatis insert
├── [课程模块] 扣减库存 → REQUIRED 加入当前事务
├── [订单模块] 扣减余额 → MANDATORY 要求事务存在
└── [消息模块] 发送通知 → REQUIRES_NEW 独立事务
5. [事务] commit → 数据落地;rollback → 全部回滚
6. [AOP] @LogAspect 记录耗时和结果
7. [Controller] 返回 ApiResponse<OrderVO>
四、30 道 Spring 综合面试题库
覆盖 6 期内容,按模块分类。
IoC / DI(第1期)
Q1 :Spring IoC 容器的启动流程?
A :new AnnotationConfigApplicationContext(Config.class) → 注册配置类 → refresh() → invokeBeanFactoryPostProcessors 解析 @ComponentScan 和 @Bean → finishBeanFactoryInitialization 实例化所有单例 Bean。全部单例 Bean 在容器启动时创建(非懒加载),通过三级缓存解决循环依赖。
Q2 :@Autowired 和 @Resource 的区别?
A :@Autowired 是 Spring 注解,默认按类型注入,配合 @Qualifier 按名称;@Resource 是 JSR-250 标准,默认按名称,找不到再按类型。@Autowired 可标记 required=false。
Q3 :Spring 如何解决循环依赖?
A :三级缓存。一级 singletonObjects(成品)、二级 earlySingletonObjects(半成品)、三级 singletonFactories(ObjectFactory)。A 创建时提前暴露 ObjectFactory 到三级缓存 → B 创建时依赖 A,从三级缓存获取 A 的早期引用 → B 创建完成 → A 继续完成创建。
Q4 :@Configuration 和 @Component 的区别?
A :@Configuration 标注的类会被 CGLIB 增强,@Bean 方法内部调用其他 @Bean 方法时返回的是容器中的同一个实例(单例保证)。@Component 没有这个增强,内部调用 @Bean 方法会创建新对象。
Q5 :FactoryBean 和 BeanFactory 的区别?
A :BeanFactory 是 IoC 容器的顶层接口(工厂的工厂)。FactoryBean 是生产特定类型 Bean 的工厂,常用于框架集成(如 SqlSessionFactoryBean 生产 SqlSession)。
自动配置(第2期)
Q6 :@SpringBootApplication 包含哪三个注解?
A :@SpringBootConfiguration(= @Configuration)、@EnableAutoConfiguration(自动配置核心)、@ComponentScan(组件扫描)。
Q7 :自动配置的实现原理?
A :@EnableAutoConfiguration → @Import(AutoConfigurationImportSelector.class) → 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports → 加载所有自动配置类 → 每个配置类用 @ConditionalOnClass、@ConditionalOnMissingBean 等条件注解判断是否生效。
Q8 :如何自定义一个 Starter?
A :① 创建 xxx-spring-boot-autoconfigure 模块,写自动配置类用 @ConditionalOnClass 控制生效条件;② 创建 xxx-spring-boot-starter 模块(空项目,引入 autoconfigure 模块);③ 在 autoconfigure 的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中声明配置类全限定名。
Spring MVC(第3期)
Q9 :DispatcherServlet 的处理流程?
A :请求 → HandlerMapping 找到 Handler(Controller 方法)→ HandlerAdapter 执行 → HandlerMethodArgumentResolver 解析参数(@RequestBody / @RequestParam 等)→ 执行 Controller 方法 → HttpMessageConverter 序列化返回值 → 返回响应。中途经过 HandlerInterceptor 的 preHandle / postHandle / afterCompletion。
Q10 :拦截器(Interceptor)和过滤器(Filter)的区别?
A:Filter 是 Servlet 规范,在请求进入 Servlet 前执行,不感知 Spring 上下文;Interceptor 是 Spring 机制,在 HandlerMapping 之后、Controller 执行前后执行,能拿到 HandlerMethod 和 Spring Bean。Filter 在最外层,Interceptor 在 DispatcherServlet 内部。
Q11 :@RequestBody 和 @ResponseBody 的工作原理?
A :通过 HttpMessageConverter 做序列化/反序列化。Spring Boot 默认使用 Jackson(MappingJackson2HttpMessageConverter)处理 JSON。@RequestBody 在参数解析阶段调用 read() 反序列化;@ResponseBody 在返回值处理阶段调用 write() 序列化。
AOP(第4期)
Q12 :JDK 动态代理和 CGLIB 的区别,Spring 如何选择?
A :JDK 基于接口 + Proxy.newProxyInstance + InvocationHandler,目标类必须实现接口;CGLIB 基于继承 + Enhancer + MethodInterceptor,通过生成子类覆写方法实现。Spring Boot 2.x 默认用 CGLIB。如果目标类实现了接口,也可以显式设置 spring.aop.proxy-target-class=false 来用 JDK 代理。
Q13 :同一个切面中多个通知的执行顺序?
A :@Around start → @Before → 目标方法 → @AfterReturning / @AfterThrowing → @After → @Around end。@After 无论异常都会执行(类似 finally),@AfterReturning 只在正常返回时执行。
Q14 :多切面作用在同一方法时,执行顺序如何控制?
A :通过 @Order(n) 控制,数值越小优先级越高。@Around 是嵌套结构------外层切面的 Around 包裹内层切面的 Around,最后才到目标方法。
事务(第5期)
Q15 :@Transactional 失效的常见场景(至少说 3 个)?
A :① 自调用(this.method() 不经过代理);② 方法非 public;③ 异常被 try-catch 吞掉;④ Checked Exception 默认不回滚,需 rollbackFor = Exception.class;⑤ 多线程/@Async 场景(ThreadLocal 线程隔离)。
Q16 :7 种传播行为,至少说 3 个常用的?
A :REQUIRED(默认,同生共死)、REQUIRES_NEW(独立事务,挂起外部)、NESTED(保存点,部分回滚)。前两者高频,NESTED 依赖 JDBC savepoint。
Q17 :REQUIRED 和 REQUIRES_NEW 的本质区别?
A :REQUIRED 加入外部事务,共用同一个 Connection,同一批操作同生共死。REQUIRES_NEW 挂起外部连接,从 DataSource 获取新连接,开启独立事务------子事务的提交/回滚完全不影响外部。
Q18 :事务怎么做到线程隔离的?为什么多线程会失效?
A :TransactionSynchronizationManager 用 ThreadLocal<Map<Object, Object>> 存储当前线程的 DataSource→Connection 映射。新线程拿不到父线程的 ThreadLocal 数据,自然不在同一事务中。
工程实践(第6期)
Q19 :你们项目的分层架构是怎样的?
A:Controller → Service → DAO/Mapper → DB。Controller 负责参数校验和统一返回,Service 负责业务逻辑和事务管理,DAO 负责数据访问。横切关注点(日志、异常、权限)通过 AOP 和全局异常处理统一管理。
Q20 :线上 CPU 飙升,你的排查步骤?
A :top -Hp PID 找到高 CPU 线程 → printf '%x' TID 转十六进制 → jstack PID | grep 十六进制 定位代码 → 或者 Arthas thread -n 3 一键看最忙线程。常见原因:死循环、正则回溯、频繁 Full GC。
Q21 :线上内存泄漏怎么排查?
A :jmap -histo:live PID | head -20 看存活对象分布;如果不明显,jmap -dump 导出堆快照,MAT 的 Dominator Tree 分析最大占用。注意 ThreadLocal 没 remove 导致的内存泄漏------tomcat 线程池复用导致线程不消亡。
Q22 :慢 SQL 排查流程?
A :SHOW FULL PROCESSLIST 看正在执行的 SQL → 启用慢查询日志 long_query_time=1 → EXPLAIN 分析执行计划,关注 type(避免 ALL)、key(必须有索引)、rows(越小越好)、Extra(避免 filesort/temporary)→ 加索引、优化 JOIN、避免 SELECT *。
Q23 :全局异常处理怎么做?
A :@RestControllerAdvice + @ExceptionHandler。自定义 BusinessException(code, message),各层统一 throw,由全局处理器转为 ApiResponse.error() 返回。Controller 层零 try-catch。
Q24 :Spring Security + JWT 的认证流程?
A :登录接口 → 验证用户名密码 → 生成 JWT token(含 userId、角色、过期时间)→ 返回 token。后续请求在 Header 带 Authorization: Bearer token → JWT 过滤器解析 token → 验证签名和过期时间 → 将用户信息放入 SecurityContext。
Q25 :Redis 在你们项目中的使用场景?
A :① 课程详情缓存(@Cacheable,TTL 30分钟);② 分布式锁(SETNX,下单防超卖);③ 用户 session 存储(JWT 黑名单);④ 排行榜(ZSet,按学习时长排序)。
综合追问链
Q26 :从浏览器输入 URL 到 Spring Boot 返回 JSON,中间经过哪些层?
A:Nginx 反向代理 → Tomcat 线程池接收 → Filter Chain → DispatcherServlet → HandlerMapping → HandlerInterceptor.preHandle → Controller 方法(参数解析 + 校验)→ Service(事务、缓存、AOP)→ DAO → DB → 返回值序列化(Jackson)→ HandlerInterceptor.postHandle/afterCompletion → Response。
Q27 :Spring 中 Bean 的生命周期?
A :实例化 → 属性填充(依赖注入)→ BeanNameAware / BeanFactoryAware → BeanPostProcessor.postProcessBeforeInitialization → @PostConstruct / InitializingBean.afterPropertiesSet → BeanPostProcessor.postProcessAfterInitialization(AOP 代理在此生成)→ 就绪 → 容器关闭时 @PreDestroy / DisposableBean.destroy。
Q28 :Spring Boot 如何实现 "约定优于配置"?
A:通过自动配置机制。Spring Boot 预设了合理的默认值(如 HikariCP 默认最大连接数 10,Tomcat 默认端口 8080),开发者只需在 application.yml 中覆盖需要的配置项。这种"默认即最佳实践"的理念大大减少了 XML 配置和样板代码。
Q29 :你对 Spring 6 / Spring Boot 3.x 的升级了解吗?
A :最大变化是基线升级到 JDK 17 和 Jakarta EE 9(javax.* → jakarta.*)。AOT 编译和 GraalVM Native Image 支持是性能亮点。此外 spring.factories 改为 AutoConfiguration.imports 文件格式。如果你的项目还在 JDK 8/11,升级时需要处理包名变更和弃用 API。
Q30 :如果让你从零搭建一个 Spring Boot 项目,你的技术选型清单?
A:Spring Boot 3.x + JDK 17 + MySQL 8.x + MyBatis-Plus + Redis + RabbitMQ + JWT + Actuator + Prometheus + Grafana。分层:Controller → Service → DAO。横切:全局异常处理 + AOP 日志 + 统一返回体。测试:JUnit 5 + Mockito。CI/CD:GitLab CI + Docker + K8s。
五、6 期系列总览
| 期数 | 主题 | 核心知识点 |
|---|---|---|
| 第1期 | IoC / DI | 容器启动流程、Bean生命周期、三级缓存、循环依赖 |
| 第2期 | 自动配置 | Starter机制、条件注解、配置绑定、自定义Starter |
| 第3期 | Spring MVC | DispatcherServlet、拦截器vs过滤器、RESTful、参数解析 |
| 第4期 | AOP | JDK vs CGLIB、切面通知顺序、@Order源码走读 |
| 第5期 | 事务 | 7种传播行为、5大失效场景、TransactionInterceptor源码 |
| 第6期 | 工程综合 | 分层架构、异常处理、排查实战、30道面试题、项目复盘 |
学习路线建议:按顺序阅读,第1-3期打基础,第4-5期攻源码,第6期做串联和面试冲刺。