Lombok库中的 @RequiredArgsConstructor 注解


@RequiredArgsConstructorLombok 库提供的一个注解,用于自动生成包含 “必要字段” 的构造函数,从而减少手动编写构造函数的样板代码。

一、核心作用

@RequiredArgsConstructor 会为类中所有final 修饰@NonNull 注解标记的字段,生成一个对应的构造函数(参数顺序与字段在类中的声明顺序一致)。

  • “必要字段” 定义:
    • final 修饰的字段(必须初始化,否则编译报错)。
    • @lombok.NonNull 注解标记的字段(即使不是 final,也被视为 “必要”,需在构造时赋值)。

二、基本用法

1. 引入 Lombok 依赖

使用前需在项目中引入 Lombok 依赖(以 Maven 为例):

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <optional>true</optional>
</dependency>

2. 代码示例(对比使用前后)

不使用 @RequiredArgsConstructor,需手动编写构造函数:

import lombok.NonNull;

public class UserService {
    private final UserDao userDao; // final 字段(必要)
    @NonNull private String appName; // @NonNull 标记(必要)
    private int maxRetry; // 非必要字段(无 final 或 @NonNull)

    // 手动编写包含“必要字段”的构造函数
    public UserService(UserDao userDao, String appName) {
        this.userDao = userDao;
        this.appName = appName;
    }
}

使用 @RequiredArgsConstructor,Lombok 自动生成构造函数:

import lombok.RequiredArgsConstructor;
import lombok.NonNull;

@RequiredArgsConstructor // 自动生成包含 userDao 和 appName 的构造函数
public class UserService {
    private final UserDao userDao; // final 字段(被包含)
    @NonNull private String appName; // @NonNull 字段(被包含)
    private int maxRetry; // 非必要字段(不被包含)

    // Lombok 自动生成以下构造函数(无需手动编写)
    // public UserService(UserDao userDao, String appName) {
    //     this.userDao = userDao;
    //     this.appName = appName;
    // }
}

三、典型使用场景

1. 依赖注入(尤其是构造函数注入)

在 Spring 等框架中,推荐使用构造函数注入依赖(相比 @Autowired 字段注入更安全,避免循环依赖问题)。@RequiredArgsConstructor 可简化构造函数注入的代码:

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor // 自动生成包含 userDao 的构造函数
public class UserService {
    // 注入的依赖通常用 final 修饰(不可变)
    private final UserDao userDao; 
    private final OrderDao orderDao;

    // Spring 会通过 Lombok 生成的构造函数自动注入 userDao 和 orderDao
    public void queryUser(Long id) {
        userDao.findById(id);
    }
}

无需手动编写 public UserService(UserDao userDao, OrderDao orderDao) { ... },减少样板代码。

示例代码2
@Service("thumbServiceLocalCache")
@Slf4j
@RequiredArgsConstructor
public class ThumbServiceImpl implements ThumbService {

    private final UserService userService;

    private final BlogService blogService;

    private final TransactionTemplate transactionTemplate;

    private final RedisTemplate<String, Object> redisTemplate;

    private final CacheManager cacheManager;

    @Override
    public Boolean doThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new RuntimeException("参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        // 加锁
        synchronized (loginUser.getId().toString().intern()) {

            // 编程式事务
            return transactionTemplate.execute(status -> {
                Long blogId = doThumbRequest.getBlogId();
                Boolean exists = this.hasThumb(blogId, loginUser.getId());
                if (exists) {
                    throw new RuntimeException("用户已点赞");
                }

                boolean update = blogService.lambdaUpdate()
                        .eq(Blog::getId, blogId)
                        .setSql("thumbCount = thumbCount + 1")
                        .update();

                Thumb thumb = new Thumb();
                thumb.setUserId(loginUser.getId());
                thumb.setBlogId(blogId);
                boolean success = update && this.save(thumb);

                // 点赞记录存入 Redis
                if (success) {
                    String hashKey = ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId();
                    String fieldKey = blogId.toString();
                    Long realThumbId = thumb.getId();
                    redisTemplate.opsForHash().put(hashKey, fieldKey, realThumbId);
                    cacheManager.putIfPresent(hashKey, fieldKey, realThumbId);
                }
                // 更新成功才执行
                return success;
            });
        }
    }

    @Override
    public Boolean undoThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new RuntimeException("参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        // 加锁
        synchronized (loginUser.getId().toString().intern()) {

            // 编程式事务
            return transactionTemplate.execute(status -> {
                Long blogId = doThumbRequest.getBlogId();
                Object thumbIdObj = cacheManager.get(ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId(), blogId.toString());
                if (thumbIdObj == null || thumbIdObj.equals(ThumbConstant.UN_THUMB_CONSTANT)) {
                    throw new RuntimeException("用户未点赞");
                }
                boolean update = blogService.lambdaUpdate()
                        .eq(Blog::getId, blogId)
                        .setSql("thumbCount = thumbCount - 1")
                        .update();
                boolean success = update && this.removeById((Long)thumbIdObj);

                // 点赞记录从 Redis 删除
                if (success) {
                    String hashKey = ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId();
                    String fieldKey = blogId.toString();
                    redisTemplate.opsForHash().delete(hashKey, fieldKey);
                    cacheManager.putIfPresent(hashKey, fieldKey, ThumbConstant.UN_THUMB_CONSTANT);
                }
                return success;
            });
        }
    }

    @Override
    public Boolean hasThumb(Long blogId, Long userId) {
        Object thumbIdObj = cacheManager.get(ThumbConstant.USER_THUMB_KEY_PREFIX + userId, blogId.toString());
        if (thumbIdObj == null) {
            return false;
        }
        Long thumbId = (Long) thumbIdObj;
        return !thumbId.equals(ThumbConstant.UN_THUMB_CONSTANT);
    }

}

以上代码中的实现类 ThumbServiceImpl 中,没有通过 @Autowired@Resource这2个注解来注入对象。

而是通过在类中定义好要注入的对象(采用private final),然后结合 @RequiredArgsConstructor注解来生成这些要对象的构造函数,以达到将对象进行“依赖注入”。

好处:相比 @Autowired 字段注入更安全,避免循环依赖问题

比如以下对象,需要在 ThumbServiceImpl 类中调用 getLoginUser()方法获取用户信息,而该方法属于另一个类,这里就可以通过注入该类(或者说注入该对象),然后再调用该方法。

通常也可以使用 @Autowired@Resource这2个注解来注入,这里就采用另一种方式构造函数注入这个类的对象。为了方便使用,有人就封装了一个注解, 即 @RequiredArgsConstructor,这样开发人员就可以省掉显示的写构造函数的代码了。

实际编译后的代码,拿 UserService举例就是:

private final UserService userService;

public ThumbServiceImpl(UserService userService) {
        this.userService = userService;
}

其他定义的对象同理,也是会在最终的编译文件中生成这样的代码

private final UserService userService;

private final BlogService blogService;

private final TransactionTemplate transactionTemplate;

private final RedisTemplate<String, Object> redisTemplate;

private final CacheManager cacheManager;

2. 确保 “必要字段” 必须初始化

final 修饰的字段必须初始化,@RequiredArgsConstructor 强制在创建对象时为这些字段赋值,避免因遗漏初始化导致的编译错误。

例如,创建 UserService 对象时,必须传入 userDaoappName

// 正确:传入所有必要字段
UserService service = new UserService(new UserDao(), "my-app");

// 错误:缺少必要参数(编译报错)
UserService service = new UserService(); // 编译失败(无无参构造)
UserService service = new UserService(new UserDao()); // 编译失败(缺少 appName)

3. 与其他 Lombok 注解配合使用

@RequiredArgsConstructor 可与 @NoArgsConstructor(生成无参构造)、@AllArgsConstructor(生成全参构造)配合,但需注意冲突:

  • 若类中存在 final 字段,@NoArgsConstructor 需手动为 final 字段赋值(否则编译报错),通常需配合 @NoArgsConstructor(force = true)(强制生成无参构造,将 final 字段设为默认值,如 null)。
  • @AllArgsConstructor 生成包含所有字段(无论是否必要)的构造函数,而 @RequiredArgsConstructor 只包含必要字段,按需选择。

四、注意事项

  1. 仅包含 “必要字段”:非 final 且无 @NonNull 的字段不会出现在生成的构造函数中。

  2. 无参构造需显式声明@RequiredArgsConstructor 不会生成无参构造,若需要(如反序列化),需额外添加 @NoArgsConstructor

  3. 依赖 Lombok 插件:IDE 需安装 Lombok 插件(如 IntelliJ IDEA、Eclipse),否则可能提示 “找不到构造函数”(实际编译时会生成)。

  4. @NonNull 的额外作用:被@NonNull标记的字段在构造函数中会自动添加空值校验,例如:

    // Lombok 为 @NonNull 字段生成的校验逻辑
    public UserService(UserDao userDao, String appName) {
        if (appName == null) {
            throw new NullPointerException("appName is marked non-null but is null");
        }
        this.userDao = userDao;
        this.appName = appName;
    }
    

总结

@RequiredArgsConstructor 的核心价值是自动生成包含必要字段的构造函数,尤其在依赖注入场景中能大幅减少样板代码,同时通过 final@NonNull 保证字段初始化的安全性。

它是 Lombok 中提升开发效率的常用注解之一。

SpringBoot
JAVA-技能点
知识点
Java注解