Lombok原理和同时使⽤@Data和@Builder 的坑

28b368a90cf5bc1d1d0f1d4a78e6e115.jpeg

程序员的成长之路

互联网/程序员/技术/资料共享 

关注

阅读本文大概需要 2.8 分钟。

来自:https://juejin.cn/post/7103011031672176677

问题背景

Lombok使⽤ 同时使⽤@Data和@Builder ,构建无参构造器报错!编译不通过。如下图:

6c8d574ddf77404609074defadebae04.jpeg


Lombok @Data和@Builder分别单独分析用法

Lombok使⽤@Data可以⽣成⽆参构造和类⾥⾯所有属性的getter/setter⽅法。可以简化我们代码的开发。(需要安装Lombok插件和引⼊Lombok依赖)。

例如下⾯的⼀个实体类,引⼊Lombok后,可以⾃动⽣成GET/SET⽅法和⽆参构造函数。

743b28964aecaffdbdb49d9cb40df30b.jpeg

编译后的class为:可以看到不仅帮我们生成了get和set ,同时也有默认的无参构造器

dc063cd6ba578aaee01edbcc63a89519.jpeg

那么怎么自动生成有参构造器呢?使用@Builder注解,将会帮助我们⽣成全属性的构造⽅法。

d1d50a94c1d1a208d1de72b1d17ca701.jpeg

编译后的class为:可以看到 已经帮我们构建好了全属性的构造方法,但是如果值只引用@Builder注解是无法生成get和set的。

2e611aac88f71387735b2d6f890a1b47.jpeg

但是如果同时使⽤@Data和@Builder的话,可以看出尽管⽣成了GET/SET⽅法,但是⽆参构造⽅法没有了,这显然是不能接受的,因为很多框架都会调⽤⽆参构造去创建对象。

9db898a6025230d45b5581b2178a9785.jpeg

编译后的class:

bbdd2d18c40d89e91deb23d1c24bd447.jpeg

我们尝试在Tet1类,⼿动添加⽆参构造⽅法。编译发现报错不通过:

d40589725141e4d9af0469a33804c140.jpeg

解决方法

方法一

Lombok同时使⽤@Data和@Builder的时候,如果要⽣成⽆参构造,需要在代码⾥⾯⼿动引⼊注解@Tolerate,让Lombok在⽣成类的时候,对指定的构造函数不感知。

b75c110086ad82645340716e87b0e7f9.jpeg

方法二

直接使用无参构造器+有参构造器的方式,@RequiredArgsConstructor 来构建有参,@NoArgsConstructor来构建无参构造器,如图所示:

e75b82ec45882206c2f9143f0b94726e.jpeg

编译后效果:

455848ac819478bcc6ef1f1596fae590.jpeg

Lombok原理

Java的编译分为以下⼏个阶段:

解析与填充符号表->注解处理->分析与字节码⽣成->⽣成⼆进制class⽂件。

  • Lombok 使⽤的是 JDK 6 实现的 JSR 269: Pluggable Annotation Processing API (编译期的注解处理器),它是在编译期时把 Lombok 的注解代码,转换为常规的 Java ⽅法⽽实现注⼊。

  • 在编译期阶段,当 Java 源码被抽象成语法树 (AST) 之后,Lombok 会根据⾃⼰的注解处理器动态的修改AST,增加新的代码 (节点),在这⼀切执⾏之后,再通过分析⽣成了最终的字节码 (.class) ⽂件,这就是Lombok 的执⾏原理。

可以借助注解处理器实现⼀个简单的 Setter,我们的实现步骤是:

  • ⾃定义⼀个注解标签接⼝,并实现⼀个⾃定义的注解处理器;

  • 利⽤ tools.jar 的 javac api 处理 AST (抽象语法树)3. 使⽤⾃定义的注解处理器编译代码。

1.定义⾃定义注解和注解处理器

⾸先创建⼀个 MySetter.java ⾃定义⼀个注解,代码如下:

ee967a5a130804444cd7e6fe70d7e3b4.jpeg

再实现⼀个⾃定义的注解处理器,代码如下:

38454ef16e0f805937918bb59312ce85.jpega15d652ee8c63848d53796f914d62c2f.jpegcbb8427272588eca893aed7456e7dcb1.jpeg

测试类如下:

24334d8f2a2d803f55990a1a3d8fd6ed.jpeg

2.对注解处理器进⾏编译,随后使⽤注解处理器对类进⾏编译

⾸先需要先对注解处理器进⾏编译(javac -cp ⽤于引⼊第三⽅jar包进⾏编译)

d3a3be0815aea812879d2e5909ed0f47.jpeg

然后使⽤注解处理器对这个Person测试类进⾏编译:

这时候再看⽣成的Person.class,可以发现Setter⽅法已经⽣成了:

e9c423dd150dc16473b159c9548a0ca6.jpeg

0cbbb93bd67a3f8f218ad90a79752942.jpeg


总结

当然尽管测试类已经⽣成Setter⽅法,但是因为是在编译时期⽣成的,因此我们在开发的时候是没法直接调⽤Setter⽅法的,因此Lombok提供了插件机制,⽅便我们在开发的时候可以直接去调⽤Lombok的特性。

<END>

推荐阅读:

知乎高赞:计算机专业会不会成为下一个土木?

多线程使用不当导致的 OOM

互联网初中高级大厂面试题(9个G)
内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper......等技术栈!
⬇戳阅读原文领取!                                  朕已阅
### Lombok 中 `@Builder` `@Accessors` 注解详解 #### 使用方法及功能描述 #### @Builder 注解 `@Builder` 是 Lombok 提供的一个强大工具,用于简化对象的创建过程。通过此注解可以在编译时自动生成一个内部静态类 Builder 及其相关的方法,从而实现流畅的 API 设计模式。 当与 `@Data` 结合使用时需要注意添加无参构造函数 (`@NoArgsConstructor`) 全参构造函数 (`@AllArgsConstructor`) 来确保正常工作[^1]。下面是一个简单的例子展示如何利用 `@Builder` 创建并初始化对象: ```java import lombok.Builder; import lombok.Data; @Data @NoArgsConstructor @AllArgsConstructor @Builder public class Person { private String firstName; private String lastName; } ``` 这样就可以像下面这样轻松地实例化对象了: ```java Person person = Person.builder() .firstName("John") .lastName("Doe") .build(); ``` #### @Accessors 注解 另一方面,`@Accessors` 主要用来定制 getter/setter 方法的行为。比如可以通过设置前缀参数让生成的方法忽略特定前缀字符;也可以开启链式调用特性使得每次 setter 调用返回当前对象本身以便连续赋值操作而不需要额外变量存储中间状态[^3]。 这里有一个具体的案例说明带有前缀配置的 `@Accessors` 如何影响成员变量访问器名称: ```java import lombok.Accessors; import lombok.Getter; import lombok.Setter; @Getter @Setter @Accessors(prefix = "p") class User { private Long pId; private String pName; } // 产生的 Getter/Setter 将会是 getId(), setId(Long), getName(), setName(String) ``` 对于启用链条特性的场景,则有如下写法: ```java import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) static class EmsUser { private String email; private String mobile; } EmsUser user = new EmsUser().setEmail("example@example.com").setMobile("1234567890"); ``` #### 关键差异比较 | 特性 | @Builder | @Accessors | |--------------------|---------------| | **主要用途** | 构建复杂不可变对象 | 定制 Getters/Setters 行为 | | **链式调用支持** | 自动提供 | 需显式声明 (chain=true) | | **默认值设定** | 支持 | 不直接涉及 | | **性能开销** | 较高(因为引入了额外 builder 类) | 更低 | 综上所述,在选择这两个注解之一之前应该考虑具体需求以及项目上下文中哪个更适合解决问题[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值