Spring 八股:Bean 生命周期、循环依赖与事务失效场景
八股的读法改造版:注解背后是代理不是魔法——Spring 八成的事故源于绕开代理或误读代理,事务失效八场景只是样本库。
附件
- fig-spring-bean-lifecycle.png(218 KB)下载
Spring 八股:Bean 生命周期、循环依赖与事务失效场景
主线:注解背后是代理,不是魔法——Spring 的事故大多源于绕开了代理或误读了代理,事务失效八场景只是这本公理的样本库。
一、公理与推论
接受「每个受注解保护的方法调用都要经过同一个代理对象」这条前提后,事务失效、AOP 自调用失效都不再是玄学,而是同一公理的直接推论。读这类八股先抓机制,再背现象——顺序不能反。
二、Q&A 速答
Q:Bean 生命周期? 实例化 → 属性填充 → Aware 回调 → BeanPostProcessor 前置 → 初始化三选一(@PostConstruct / afterPropertiesSet / init-method)→ BPP 后置(AOP 代理在此织入) → 使用 → 销毁回调。分水岭在 BPP 后置:代理生成在这里,所以「代理能否被提前引用」成为后一级考题。
Q:循环依赖三级缓存怎么解? 一级成品、二级早期引用、三级 ObjectFactory——B 依赖 A 时由工厂拿到同一个代理,不重复创建。构造器循环依赖无解(三级缓存的前提是先有可暴露的实例),prototype 亦然;工程答案是换依赖方向。
Q:AOP 用什么代理?什么时候失效? 有接口 JDK 动态代理,无接口 CGLIB(Spring Boot 2.x 起默认全 CGLIB)。自调用 this.xxx() 绕过代理——本库锚点:审计的 @AuditLog 切面与 MVC 兜底拦截器是双入口,为防重复记录,拦截器必须同时排除自查询路径与已被注解覆盖的方法(防重三件套)。
Q:事务失效八场景? 1 方法非 public;2 同类自调用;3 异常被 try 吞掉;4 默认只回滚 RuntimeException/Error(checked 必须 rollbackFor);5 传播属性误解(REQUIRES_NEW 挂起外层而非嵌套);6 事务内开线程另起连接;7 rollbackOnly 标记冲突(UnexpectedRollbackException);8 存储引擎不支持事务。八个场景全部能由「代理」公理推出——自调用失效就是 this 不过代理。
工程锚点:审计日志刻意异步批量落库、不走主事务——日志写失败不能连坐业务回滚,这是传播行为语义最真实的运用。
Q:MVC 请求链路? Filter(TraceId / TokenContext 清理点)→ DispatcherServlet → HandlerMapping → Interceptor pre / post / afterCompletion(兜底拦截器挂点)→ 控制器 → @RestControllerAdvice 统一 ErrorResponse。
三、思辨:魔法与机制的信任边界
「注解黑箱」式信任会让开发者丧失追问能力。事务失效八场景的真正价值,是教会人每个注解背后都站着一张机制图:代理是机制的机制,但机制终有边界——循环依赖无解、自调用失效、TransactionManager 单值语义。把这些边界写进肌肉记忆,比在事故后要求框架「更聪明一些」现实得多。框架不是魔法,是明码标价的机制。
四、回到现实
三个动作:同类调用先怀疑代理;排查事务先过八场景清单;定制切面先想清「与拦截器的职责划分」。