@RequiredArgsConstructor 是 Lombok 库提供的一个注解,用于自动生成包含 “必要字段” 的构造函数,从而减少手动编写构造函数的样板代码。
@RequiredArgsConstructor 会为类中所有被 final 修饰或被 @NonNull 注解标记的字段,生成一个对应的构造函数(参数顺序与字段在类中的声明顺序一致)。
final 修饰的字段(必须初始化,否则编译报错)。@lombok.NonNull 注解标记的字段(即使不是 final,也被视为 “必要”,需在构造时赋值)。使用前需在项目中引入 Lombok 依赖(以 Maven 为例):
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<optional>true</optional>
</dependency>
不使用 @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;
// }
}
在 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) { ... },减少样板代码。
@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;
被 final 修饰的字段必须初始化,@RequiredArgsConstructor 强制在创建对象时为这些字段赋值,避免因遗漏初始化导致的编译错误。
例如,创建 UserService 对象时,必须传入 userDao 和 appName:
// 正确:传入所有必要字段
UserService service = new UserService(new UserDao(), "my-app");
// 错误:缺少必要参数(编译报错)
UserService service = new UserService(); // 编译失败(无无参构造)
UserService service = new UserService(new UserDao()); // 编译失败(缺少 appName)
@RequiredArgsConstructor 可与 @NoArgsConstructor(生成无参构造)、@AllArgsConstructor(生成全参构造)配合,但需注意冲突:
final 字段,@NoArgsConstructor 需手动为 final 字段赋值(否则编译报错),通常需配合 @NoArgsConstructor(force = true)(强制生成无参构造,将 final 字段设为默认值,如 null)。@AllArgsConstructor 生成包含所有字段(无论是否必要)的构造函数,而 @RequiredArgsConstructor 只包含必要字段,按需选择。仅包含 “必要字段”:非 final 且无 @NonNull 的字段不会出现在生成的构造函数中。
无参构造需显式声明:@RequiredArgsConstructor 不会生成无参构造,若需要(如反序列化),需额外添加 @NoArgsConstructor。
依赖 Lombok 插件:IDE 需安装 Lombok 插件(如 IntelliJ IDEA、Eclipse),否则可能提示 “找不到构造函数”(实际编译时会生成)。
@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 中提升开发效率的常用注解之一。