欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > 探索 Lombok注解 之 @RequiredArgsConstructor :原理、实践与问题解决

探索 Lombok注解 之 @RequiredArgsConstructor :原理、实践与问题解决

2025/9/25 18:21:57 来源:https://blog.csdn.net/q2qwert/article/details/140683375  浏览:    关键词:探索 Lombok注解 之 @RequiredArgsConstructor :原理、实践与问题解决

文章目录

  • 一、什么是@RequiredArgsConstructor?
  • 二、@RequiredArgsConstructor的使用方式
    • 1、引入依赖
    • 2、代码示例
      • 2.1、常规 @Autowired注入:
      • 2.2、常规 @Autowired注入:
      • 2.3、lombok中的**RequiredArgsConstructor注入:
  • 三、@RequiredArgsConstructor的实现原理
  • 四、@RequiredArgsConstructor 在使用中遇到的几个问题
    • 1、循环依赖问题
    • 2、@Qualifier失效问题
  • 五、小结

一、什么是@RequiredArgsConstructor?

注解@RequiredArgsConstructor 是 Lombok 提供的一个注解,其主要作用在于简化 @Autowired 的书写过程。在编写 Controller 层或 Service 层代码时,常常需要注入众多的 mapper 接口或 service 接口。若每个接口都使用 @Autowired 进行标注,代码会显得繁琐。而 @RequiredArgsConstructor 注解能够替代 @Autowired 注解,但需注意,在类上添加 @RequiredArgsConstructor 时,需要注入的类必须使用 final 进行声明。

二、@RequiredArgsConstructor的使用方式

1、引入依赖

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

2、代码示例

2.1、常规 @Autowired注入:

/*** @author fhey*/
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/getUser")public User getUser(){User user = userService.getUser("1");return user;}
}

2.2、常规 @Autowired注入:

/*** @author fhey*/
@RestController
@RequestMapping("/user")
public class UserController {private UserService userService;public void setUserService(UserService userService) {this.userService = userService;}@GetMapping("/getUser")public User getUser(){User user = userService.getUser("1");return user;}
}

2.3、lombok中的**RequiredArgsConstructor注入:

/*** @author fhey*/
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {private final UserService userService;@GetMapping("/getUser")public User getUser(){User user = userService.getUser("1");return user;}
}

可以看到,使用 @RequiredArgsConstructor 省去了 @Autowired@Resource 注解。

三、@RequiredArgsConstructor的实现原理

您是否好奇 @RequiredArgsConstructor 是如何运作的?让我们深入探究一番!

​ 在编译阶段,Lombok 借助“AST transformation”(抽象语法树转换)这一技术来修改 Java 源代码。这是一种在编译期间处理源代码的手段,它不会对运行时的性能造成影响,却能在生成的字节码中留下 Lombok 的运作痕迹。

​ 当在类上使用 @RequiredArgsConstructor 注解时,Lombok 会对类中的所有 final 字段进行观察,并在编译期间生成相应的构造函数。这个生成的构造函数会将这些 final 字段作为参数,并在构造函数内部完成这些字段的初始化。

​ 大致的实现步骤如下:

  1. 收集所有的 final 字段。

  2. 生成一个包含这些 final 字段作为参数的构造函数。

  3. 在构造函数内,将参数的值赋给对应的字段。

以下是一个示意代码(需注意,这只是为了演示,实际实现要复杂得多):

import java.lang.reflect.Field;public class LombokMagic {public static <T> void createRequiredArgsConstructor(T instance) {for (Field field : instance.getClass().getDeclaredFields()) {if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) {try {field.setAccessible(true);// 输出日志,告诉大家正在进行魔法操作System.out.println("Abracadabra! Initializing field: " + field.getName());// 初始化final字段,这里为了简化演示使用了空字符串和0field.set(instance, field.getType() == String.class ? "" : 0);} catch (IllegalAccessException e) {e.printStackTrace();}}}}public static void main(String[] args) {// 创建一个类实例ExampleClass example = new ExampleClass();// 使用Lombok的魔法方法,模拟@RequiredArgsConstructor的行为createRequiredArgsConstructor(example);// 魔法完成!现在example对象的final字段已被初始化。}
}class ExampleClass {final String name;final int age;// 其他字段...
}

当然,实际的 Lombok 实现要复杂得多,涉及到 AST 处理、字节码生成等高深技术。但希望这个简化的模拟能让您对其有个大致的概念!

四、@RequiredArgsConstructor 在使用中遇到的几个问题

1、循环依赖问题

在实际运用中,有时会出现循环依赖的状况,即 A 引用了 B,而 B 又引用了 A。虽然 Spring 通过三级缓存的方式解决了大部分循环依赖问题,但由构造函数注入导致的循环依赖问题却未得到解决,而 @RequiredArgsConstructor 恰好是基于构造函数实现的注入。

因此,在两个类相互引用时使用 @RequiredArgsConstructor 必定会报错。我们新建两个类 LoginServiceUserService,并让它们相互引用以重现这个场景。

@Service("systemUserService")
@RequiredArgsConstructor
public class SystemUserServiceImpl implements UserService{private final LoginService loginService;@Overridepublic User getUser(String userId) {User user = new User();user.setId(userId);user.setName("fhey");return user;}
}@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService{private final UserService userService;@Overridepublic String login(String userId, String password) {return null;}
}

项目启动时可以看到直接报错了:

***************************
APPLICATION FAILED TO START
***************************Description:The dependencies of some of the beans in the application context form a cycle:userController defined in file [D:\Project\test\fhey-test\api-test\target\classes\com\fhey\test\controller\UserController.class]
┌─────┐
|  systemUserService defined in file [D:\Project\test\fhey-test\api-test\target\classes\com\fhey\test\service\user\SystemUserServiceImpl.class]
↑     ↓
|  loginServiceImpl defined in file [D:\Project\test\fhey-test\api-test\target\classes\com\fhey\test\service\user\LoginServiceImpl.class]

那么该如何解决这个问题呢?

常规 @Autowired 注入解决循环依赖的方式通常是将 @Autowired@Lazy 组合使用,示例代码如下:

@Service("systemUserService")
public class SystemUserServiceImpl implements UserService{@Autowired@Lazyprivate final LoginService loginService;@Overridepublic User getUser(String userId) {User user = new User();user.setId(userId);user.setName("fhey");return user;}
}@Service
public class LoginServiceImpl implements LoginService{@Qualifier("customerUserService")private final UserService userService;@Overridepublic String login(String userId, String password) {return null;}
}

@RequiredArgsConstructor 解决循环依赖同样使用 @Lazy ,为 @RequiredArgsConstructor 添加一个 onConstructor_ = {@Lazy} 参数,示例代码如下:

@Service("systemUserService")
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class SystemUserServiceImpl implements UserService{private final LoginService loginService;@Overridepublic User getUser(String userId) {User user = new User();user.setId(userId);user.setName("fhey");return user;}
}@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class LoginServiceImpl implements LoginService{private final UserService userService;@Overridepublic String login(String userId, String password) {return null;}
}

2、@Qualifier失效问题

假设存在一个接口有两个实现类,使用 @Autowired 注入时,面对这种情况通常会使用 @Qualifier 来指定具体使用哪一个实现类。然而,使用 @RequiredArgsConstructor 时,会导致 @Qualifier 失效。

首先,创建一个接口 UserService ,代码如下:

import com.fhey.test.po.User;public interface UserService {User getUser(String userId);
}

接着,新建两个实现类 SystemUserServiceImplCustomerUserServiceImpl ,代码如下:

import com.fhey.test.po.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;@Service("systemUserService")
@RequiredArgsConstructor
public class SystemUserServiceImpl implements UserService{@Overridepublic User getUser(String userId) {User user = new User();user.setId(userId);user.setName("fhey");return user;}
}import com.fhey.test.po.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;@Service("customerUserService")
@RequiredArgsConstructor
public class CustomerUserServiceImpl implements UserService{@Overridepublic User getUser(String userId) {User user = new User();user.setId(userId);user.setName("fhey");return user;}
}

然后,新建一个控制器 UserController ,代码如下:

import com.fhey.test.po.User;
import com.fhey.test.service.user.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {@Qualifier("systemUserService")private final UserService userService;@GetMapping("/getUser")public User getUser(){User user = userService.getUser("1");return user;}
}

最后启动项目,此时项目启动报错提示:

***************************
APPLICATION FAILED TO START
***************************Description:The dependencies of some of the beans in the application context form a cycle:userController defined in file [D:\Project\test\fhey-test\api-test\target\classes\com\fhey\test\controller\UserController.class]
┌─────┐
|  systemUserService defined in file [D:\Project\test\fhey-test\api-test\target\classes\com\fhey\test\service\user\SystemUserServiceImpl.class]
↑     ↓
|  loginServiceImpl defined in file [D:\Project\test\fhey-test\api-test\target\classes\com\fhey\test\service\user\LoginServiceImpl.class]
└─────┘

这是典型的循环依赖报错,表明 @Qualifier 这个注解并未生效。实际上在 IDEA 中也有提示 "Lombok does not copy the annotation ‘org.springframework.beans.factory.annotation.Qualifier’ into the constructor ",这意味着 Lombok 没有将 @Qualifier 注解复制到构造器中,从而导致 @Qualifier 注解失效。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I9ZRhrDF-1721881959455)(https://i-blog.csdnimg.cn/direct/95c9a47f8fb84746b792c7e8eb67fa51.png)]

那么应该如何解决这个问题呢?

解决办法如下:
在项目根目录下创建一个 lombok.config 文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mq2Hg0mU-1721881959460)(https://i-blog.csdnimg.cn/direct/c995325ce0ed4d1da29725ea1a2e1a48.png)]

然后在里面添加一项配置:将 @Qualifier 添加进允许复制的注解列表中。

lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier

随后删除 target 文件或者执行 mvn clean ,重新构建项目即可。

五、小结

本文详细介绍了 Lombok 中的 @RequiredArgsConstructor 注解。首先说明了其作用是简化 @Autowired 的书写,然后阐述了其使用方式,包括引入依赖和具体的代码示例。接着深入探讨了其实现原理,即通过在编译时利用“AST transformation”技术根据类中的 final 字段生成构造函数并进行初始化。同时,还指出了在使用中可能遇到的两个问题,一是循环依赖问题,解决方法是与 @Lazy 结合使用;二是 @Qualifier 失效问题,可通过在项目根目录创建 lombok.config 文件并添加相关配置来解决。

实现原理部分参考文章:一行注解,省却百行代码:深度解析@RequiredArgsConstructor的妙用-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词