@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 构造注入 → 不用写
相关推荐
咩咦10 天前
C++学习笔记28:静态成员应用:不用循环求1到n的和
c++·学习笔记·类和对象·static·构造函数·oj·静态成员
咩咦15 天前
C++学习笔记24:构造函数初始化列表
c++·学习笔记·类和对象·构造函数·初始化列表·const引用
咩咦17 天前
C++学习笔记16:构造函数
c++·学习笔记·类和对象·构造函数·默认构造函数
咩咦18 天前
C++学习笔记17:析构函数
c++·学习笔记·类和对象·构造函数·析构函数·动态内存
jieyucx21 天前
Go 语言进阶:构造函数、父子结构体与组合复用详解
服务器·算法·golang·继承·结构体·构造函数
叼烟扛炮1 个月前
C++ 知识点12 构造函数
开发语言·c++·算法·构造函数
Tairitsu_H1 个月前
C++:构造函数与初始化列表详解
开发语言·c++·构造函数
网域小星球2 个月前
C++ 从 0 入门(三)|类与对象基础(封装、构造 / 析构函数,面试必考)
开发语言·c++·面试·构造函数·析构函数
CoderMeijun2 个月前
C++构造与析构:对象的生与死
c++·面向对象·构造函数·析构函数·c++基础