Spring 高频坑有哪些?循环依赖、事务代理与懒加载如何规避?

结论先行:Spring 开发里绝大多数“怪问题”源于同一个底层事实——注解增强走代理,容器管理生命周期。三大高频坑:① 启动循环依赖报错(Boot 2.6+ 默认禁止);② @Transactional/@Async/@Cacheable 因自调用或非 public 方法失效;③ 懒加载把“启动错误”推迟成“运行期事故”。规避的核心是:构造器注入 + 分层清晰 + 依赖外部调用。

一、启动循环依赖报错

现象:BeanCurrentlyInCreationException / “依赖形成闭环”;Boot 2.6 起默认禁止循环引用(报错提示改为 allow-circular-references 属性)。

方式说明
构造器注入循环无法解决,必须重构
setter/字段注入(单例)老版本靠三级缓存可解,Boot 2.6+ 默认仍拒绝
@Lazy 打破延迟注入代理,绕开构建期循环
正确姿势用构造器注入 + 拆分依赖方向,从根上消除
# 只建议临时过渡,不要作为常态
spring:
  main:
    allow-circular-references: true

二、事务与代理失效

典型场景:Service 内 this.doSomething() 调同类的 @Transactional 方法;方法不是 public;异常被 catch 吞掉;调用方跨线程。

@Service
public class OrderService {
    @Transactional
    public void a() { insert(); }        // 代理生效

    public void b() {
        a();                              // this.a():直调原始对象 → 事务失效!
    }
}

规避手段:

  1. 增强方法放到另一个 Bean,跨 Bean 注入调用;
  2. 自注入(@Lazy 注入自身代理)或 AopContext.currentProxy();
  3. 保证 public、异常向外抛(或 rollbackFor 指定受检异常);
  4. 事务边界只在 Service 层,别在 Controller/异步线程里开事务;
  5. Boot 3.x 默认 CGLIB 代理:final 类/方法无法代理,注意设计。

三、懒加载的坑

spring:
  main:
    lazy-initialization: true   # 全量懒加载:启动快但错误后置
  • @Lazy 局部用:Bean 首次被使用时才创建,可解决部分循环依赖,也用于“大而少用”的资源;
  • 坑:配置错误、依赖缺失在启动时发现不了,首笔流量才报错 → 线上事故;
  • 结论:默认不全局懒加载;个别 @Lazy 需确认“延迟创建”语义符合预期。

常见追问 / 记忆点

  • 追问:为什么 Boot 2.6 开始默认禁止循环依赖?答:鼓励用构造器注入与清晰分层,把设计问题在启动期暴露而非带病运行。
  • 追问:@Transactional 失效的第一排查顺序?答:看是否自调用、是否非 public、异常是否被吞、是否走代理 Bean。
  • 记忆点:代理增强靠外部调用、异常要外抛、循环依赖靠重构;懒加载是“把炸弹延后引爆”。
笔记加载中…