@RequiredArgsConstructor与@Autowired 对比

一、两种写法先摆出来

写法 1:传统 @Autowired 字段注入

java 复制代码
@RestController
public class SysUserController {

    @Autowired
    private ISysUserService userService;
    
    @Autowired
    private ISysRoleService roleService;
}

写法 2:@RequiredArgsConstructor + final 构造注入

java 复制代码
@RequiredArgsConstructor
@RestController
public class SysUserController {

    private final ISysUserService userService;
    private final ISysRoleService roleService;
}

二、为什么 Spring 官方不推荐 @Autowired 字段注入

1. 循环依赖风险更高

比如 A 依赖 B,B 依赖 A:

  • 字段注入:容易隐藏循环依赖问题,运行时才报错
  • 构造器注入:项目启动直接报错,提早暴露问题,好排查

2. 违反单一职责原则

字段注入很容易无脑往上堆 @Autowired,一个 Controller 注入十几个 Service,类臃肿、职责混乱。构造器注入参数太多,编译器看着就别扭,倒逼你拆分类。

3. 容易造成可变依赖、空指针

字段不是 final,后续可以随便改赋值:

java 复制代码
userService = null; // 随时能篡改

后面调用直接 NPE。

final + 构造注入:一旦初始化完成,永远不能被修改,更安全。

4. 单元测试难

字段注入:必须用 Spring 容器、@MockBean 才能测,脱离容器不好 new 对象。

构造器注入:

java 复制代码
// 普通 new 就能单元测试,不用启Spring容器
new SysUserController(mockUserService, mockRoleService);

纯 Java 就能测试,轻便很多

5. 依赖不透明

字段注入藏在类里面,外人看不出这个类到底依赖哪些组件 。构造器参数一目了然:参数就是所有依赖


三、@RequiredArgsConstructor + final 优势

  1. 省去手写构造方法,代码极简
  2. 天然 final 不可变,杜绝篡改和空指针
  3. Spring 4.3+ 首选推荐,官方标准写法
  4. 方便单元测试,可手动 new 传参
  5. 强制依赖明确,不会乱注入一堆组件

四、一句话选型规则

  • 业务 Controller / Service / Mapper :统一用@RequiredArgsConstructor + private final 依赖彻底不用 @Autowired

  • 只有特殊场景才偶尔用 @Autowired:静态变量注入、特殊工具类、少量非常规 Bean 注入。


五、普通类举例

为什么 SysUserImportListener 必须手写构造方法?

java 复制代码
@Slf4j
public class SysUserImportListener extends AnalysisEventListener<SysUserImportVo> {

    private final ISysUserService userService;
    private final String password;
    private final Boolean isUpdateSupport;
    private final Long operUserId;

    // 必须手写!
    public SysUserImportListener(Boolean isUpdateSupport) {
        String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey(...);
        this.userService = SpringUtils.getBean(ISysUserService.class);
        this.password = BCrypt.hashpw(initPassword);
        this.isUpdateSupport = isUpdateSupport;
        this.operUserId = LoginHelper.getUserId();
    }
}

这里有 3 个 lombok 无法处理的原因:

构造方法的参数不匹配

@RequiredArgsConstructor 只会生成 包含所有 final 字段的构造器

java 复制代码
// lombok 想生成的是这种(4个参数)
public SysUserImportListener(ISysUserService userService,
                             String password,
                             Boolean isUpdateSupport,
                             Long operUserId) {
}

但你实际需要的构造器只有 1 个参数

java 复制代码
public SysUserImportListener(Boolean isUpdateSupport) {
}

很多值不是外部传入,而是内部手动获取

  • password → 内部查配置生成
  • userService → 内部 SpringUtils 获取
  • operUserId → 内部 LoginHelper 获取

lombok 不知道这些逻辑,它只会无脑赋值。

java 复制代码
// 不能用 @RequiredArgsConstructor
private final String password;
private final Boolean isUpdateSupport;

这些值不是 Spring 注入的,是构造里自己:

  • 从配置取值
  • 加密处理
  • 获取登录用户 ID

Lombok 只会帮你按参数直接赋值 ,做不了业务逻辑,所以只能手写构造方法

这个类不是 Spring Bean

  • @RestController 是 Spring 管理的
  • SysUserImportListenerEasyExcel 手动 new 出来的对象
java 复制代码
new SysUserImportListener(true);  // 你自己创建的

Spring 不会帮你注入,所以必须自己写构造方法自己处理


六、一句话终极总结

  • Controller + @RequiredArgsConstructor + final 注入不用写构造方法,lombok 自动生成。
  • 普通类 + 自己 new + 内部动态赋值必须手写构造方法,lombok 无法处理你的业务逻辑。

总结

  1. @RequiredArgsConstructor 只负责:给所有 final 字段生成全参构造。
  2. 你的监听器 构造参数数量不对 + 内部有业务逻辑 + 不是 SpringBean必须手写构造
  3. Controller 是标准 Spring 构造注入 → 不用写
相关推荐
叼烟扛炮7 天前
C++ 知识点12 构造函数
开发语言·c++·算法·构造函数
Tairitsu_H20 天前
C++:构造函数与初始化列表详解
开发语言·c++·构造函数
网域小星球1 个月前
C++ 从 0 入门(三)|类与对象基础(封装、构造 / 析构函数,面试必考)
开发语言·c++·面试·构造函数·析构函数
CoderMeijun1 个月前
C++构造与析构:对象的生与死
c++·面向对象·构造函数·析构函数·c++基础
奶人五毛拉人一块2 个月前
C++类和对象的学习-1
c++·对象··构造函数·析构函数·运算符重载
叫我一声阿雷吧2 个月前
JS 入门通关手册(20):构造函数与原型:JS 面向对象第一课
开发语言·javascript·前端开发·前端面试·构造函数·js进阶·js面向对象
元让_vincent3 个月前
DailyCoding C++ | SLAM里的“幽灵数据”:从一个未初始化的四元数谈C++类设计
开发语言·c++·slam·构造函数·类设计·激光里程计
量子炒饭大师3 个月前
【C++入门】Cyber骇客构造器的核心六元组 —— 【类的默认成员函数】明明没写构造函数也能跑?保姆级带你掌握六大类的默认成员函数(下:运算符重载)
java·c++·dubbo·面向对象编程·构造函数·运算符重载·默认成员函数
三月微暖寻春笋5 个月前
【和春笋一起学C++】(五十)在构造函数中使用new时的注意事项
c++·new·构造函数